BZOJ2158 Crash的旅行計畫

2021-08-06 07:01:12 字數 4064 閱讀 8478

好久沒寫樹剖

+ 線段樹各種維護的題了,這題調了我整整一天。。。(膜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...