一種明顯的做法是直接樹鏈剖分然後用區間修改區間查詢樹狀陣列(我寫的這種)或者線段樹來維護吧。。這樣做是o(nlog^2n)的。
但是還可以做到o(nlogn)。首先可以發現它是單點鏈上查詢,那麼可以考慮用差分的思想,或者考慮將單點修改直接變成區間修改然後就只用單點簡單查詢了。
首先考慮單點修改,這種操作只對它所在的子樹中的所有點的答案有影響,也就是所在的子樹中每乙個點的答案+a,這個可以用dfs序列轉化為區間修改。
然後考慮對子樹x的修改,那麼對所在的子樹中某乙個點p,點p的答案+a*(d[p]-d[x]+1)=+a*d[p]+(a-a*d[x]),注意到括號中的部分是常數(即對每乙個p來說相同),用上面的單點修改即可。然後我們還需要乙個陣列b(另外乙個假設是a)維護每乙個點的d[p]被加上了多少次。這樣還是區間修改。
那麼查詢的答案就是ai+bi*d[i]了,直接上查分的樹狀陣列轉化為單點修改字首查詢。o(nlogn),這個應該是最快的寫法了。
ac**如下(樹鏈剖分+區間修改區間查詢樹狀陣列):
#include#include#include#define ll long long
#define n 100005
using namespace std;
int n,m,tot,dfsclk,a[n],fst[n],pnt[n<<1],nxt[n<<1],bg[n],ed[n],fa[n],anc[n],son[n],sz[n];
ll c[2][n];
int read()
while (ch>='0' && ch<='9')
return x*fu;
}void add(int x,int y)
void ins(int k,int x,ll t)
ll getsum(int k,int x)
void mdy(int x,int y,ll z)
ll qry(int x,int y)
void dfs(int x) }}
void divide(int x,int tp)
ed[x]=dfsclk;
}int main(){
n=read(); m=read(); int i,k,x,y; ll ans;
for (i=1; i<=n; i++) a[i]=read();
for (i=1; i
by lych
2016.3.14
BZOJ 4034 T2 樹鏈剖分解決子樹問題
樹鏈剖分有這樣乙個性質,即在剖完之後每個結點下面子樹的編號一定是連續的,那麼基於這一點,我們記錄每個結點的區間就能解決子樹更新問題 dfs序也可以解決子樹問題,但是這裡需要計算乙個貢獻值,結點的層數不好處理。include include include includeusing namespace...
bzoj4034 樹鏈剖分
time limit 10 sec memory limit 256 mb submit 7576 solved 2597 submit status discuss 有一棵點數為 n 的樹,以點 1 為根,且樹點有邊權。然後有 m 個 操作,分為三種 操作 1 把某個節點 x 的點權增加 a 操作...
11 03T2 樹鏈剖分
給你一棵樹,每次詢問樹上兩條鏈是否有交點。第一行n,表示n個結點 第二行開始n 1行倆個 數x y,表示x,y有一條邊 接下來q,表示q個詢問 接下來q行四個數a b c d,詢問a到b的鏈是否與c到d的鏈有交點 輸出q行 yes或no sample input 輸入1 1 21 3 2 42 5 ...