好久沒寫樹剖
+ 線段樹各種維護的題了,這題調了我整整一天。。。(膜sp
y大佬)
首先對於
n<=
1000
的資料,
可以直接對於每乙個詢問暴力遍歷整棵樹,複雜度o(
n2)
而對於一條鏈的情況,我們只需要用一棵線段樹
儲存每個區間的lm
x、rm
x、su
m 即可
對於區間[l
,r] 來說lm
x=ma
x ,rm
x 同理
而這三個值都是可合併的(l
mx[l
,r]=
max(
lmx[
l,mi
d],s
um[l
,mid
]+lm
x[mi
d+1,
r]))
,所以線段樹可行 然後u
pdat
e 和qu
ery 操作其實就很簡單了
複雜度o(n
logn
2)
接下來就是二叉樹和深度不超過
40 的樹的情況,其實這兩種情況完全可以合併。。
我們只需要對於每個點儲存乙個mu
ltis
et,表示每個節點的子樹中,
不包括他本身的以他的兒子為其中乙個端點的最長鏈
由於最多只有
40 層,所以每次up
date
和que
ry走過的層數不會超過
40
複雜度o(40
⋅nlo
gn2)
然後是正解,當然就是樹鏈剖分剖出一條條重鏈然後線段樹強模擬
對於每一條鏈線段樹上儲存的同樣也是lm
x、rm
x、su
m 當然線段樹只能維護重兒子的資訊,
對於每個節點還需要開乙個mu
ltis
et維護其輕兒子的資訊
於是將lmx
的定義轉為lm
x=ma
x do
wn[i
] 即是節點
i 的輕邊最長鏈長度,rm
x同理
由於每條由根到葉子節點的路徑不會經過多於lo
gn2 個輕節點
當然也不會經過多於lo
gn2 條重鏈,所以複雜度是***的
我們考慮每個點
x 出發的最長鏈的可能情況
1.該點向下不包括其本身的lm
x+va
l[x]
2.該點向上不包括其本身的rm
x+va
l[x]
3.該點向下不包括其本身的輕邊最長鏈va
l[x]
為什麼要如此繁瑣的不包括本身又+v
al[x
] 這是為了防止跳重鏈跳到了fa
[top
[x]]
時,又往下找最長鏈
當然如果跳重鏈的話時時維護乙個su
m 再搞上面三個操作即可
接下來考慮up
date
操作 這個操作其實和qu
ery 也是一樣的
先把當前這根鏈清掉,
然後往上跳重鏈即可
**如下:
#include
using
namespace
std;
#define m 100005
struct eedge[m<<1];
int head[m],tot;
int n,val[m];
void add(int a,int b);head[a]=tot++;
}int fa[m],top[m],son[m],sz[m],dfn[m],mp[m],rt[m],ttp[m],lid[m<<2],rid[m<<2],mx[m],tt,tp;
struct node
void build(int &p,int l,int r)
int mid=l+r>>1;
build(lson[p],l,mid);
build(rson[p],mid+1,r);
up(p);
}void updatesum(int x,int v,int p,int l,int r)
int mid=l+r>>1;
if(x<=mid)updatesum(x,v,lson[p],l,mid);
else updatesum(x,v,rson[p],mid+1,r);
up(p);
} void updatemx(int x,int v,int p,int l,int r)
int mid=l+r>>1;
if(x<=mid)updatemx(x,v,lson[p],l,mid);
else updatemx(x,v,rson[p],mid+1,r);
up(p);
} int querysum(int l,int r,int p,int l,int r)
int querylmx(int l,int r,int p,int l,int r)
int queryrmx(int l,int r,int p,int l,int r)
}t;void dfs(int x,int pre,int d)
}void rdfs(int x,int pre,int f));
if(x==f)
rt[x]=rt[f];
rid[rt[x]]=dfn[x];
if(son[x])rdfs(son[x],x,f);
for(int i=head[x];~i;i=edge[i].nxt));//維護輕兒子資訊
}mx[x]=lgt[x].begin()->val;
if(x==f)t.build(rt[x],lid[rt[x]],rid[rt[x]]);//建重鏈樹
}void update(int x,int v)
x=tmp;
t.updatesum(dfn[x],v,rt[x],lid[rt[x]],rid[rt[x]]);
t.updatemx(dfn[x],v+mx[x],rt[x],lid[rt[x]],rid[rt[x]]);//更新當前重鏈
val[x]=v;
while(top[x]>1));
lgt[fa[top[x]]].insert((node));//更新輕兒子資訊
mx[fa[top[x]]]=lgt[fa[top[x]]].begin()->val;
t.updatemx(dfn[fa[top[x]]],max(mx[fa[top[x]]],t.lmx[rt[top[x]]])+val[fa[top[x]]],rt[fa[top[x]]],lid[rt[fa[top[x]]]],rid[rt[fa[top[x]]]]);//更新重鏈資訊
x=fa[top[x]];
}}int query(int x)
if(top[x]==1)break;
x=top[x];
tmp+=val[fa[x]];
if(lgt[fa[x]].begin()->id==x));
max(res,lgt[fa[x]].begin()->val+tmp);
lgt[fa[x]].insert((node));
}else max(res,mx[fa[x]]+tmp);//輕邊最長鏈+val[x]
x=fa[x];
if(dfn[x]!=rid[rt[x]])max(res,t.querylmx(dfn[x]+1,rid[rt[x]],rt[x],lid[rt[x]],rid[rt[x]])+tmp);//lmx+val[x]
}return res;
}int main()
dfs(1,-1,1);
rdfs(1,-1,1);
for(;;)
else
if(str[0]=='q')
else
break;
}return
0;}
BZOJ2159 Crash 的文明世界
這篇寫差分表和斯特林數介紹的不錯 這題就是要計算這個東西 s i j 1n dist i,j ks i j 1nd ist i,j k這個東西很難維護,我們把di st i j k d is t i,j k拆一下s u v kj 0d u,v d u,v j s u v j 0 kd u v d u...
bzoj 2159 Crash 的文明世界
又來做了一次。之前寫得實在是太差了,這次寫好點吧。這裡介紹用斯特林數展開的方法 如果不會的可以先看看這裡 我們知道xn k 0 ns n k k c x,k x n sum ns n,k k c x,k xn k 0n s n,k k c x k 因此,如果想知道答案,其實就是要知道對於每乙個k k...
BZOJ 2159 Crash 的文明世界
記得去年暑假集訓的時候本來想了乙個動態點分的做法的,然後寫道一半因為某些不知名原因就沒寫了,然後就一直放著,然後發現斯特林反演真nm好寫 首先考慮用關於冪的斯特林反演 m n sum m left times i times c m i 套上去就是 ans x sum n dis i,x k sum...