點分樹,也叫動態點分治,就是對於一個樹性結構,按照重心的父子關係轉化成一顆深度嚴格為logn的樹
與普通點分治不同的是,他是動態的。。。就是可以在樹上的節點進行權值更改,然後暴力得到答案
這個暴力可不是從根節點一直往下搜,而是利用某些資料結構,或者一個比較簡單的遞推式,又或是啥的,來簡化暴力的過程
(優美的暴力)
所以我們對於重心的合理利用成了重點,當然,這也是點分治,容斥原理的利用還是非常重要的
這個就是點分樹最最最最基礎的模板題了,就是要邊地震邊修改權值
然後加上稍微簡單一點的查詢
首先我們一定是要找到lca的,不然咋求兩點之間的距離??
(個人習慣用樹鏈剖分中的輕重鏈top來求)
int siz[N],dep[N],son[N],fa[N]; void dfs1(int x,int f){ siz[x]=1; dep[x]=dep[f]+1; fa[x]=f; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f)continue; dfs1(y,x); siz[x]+=siz[y]; if(!son[x]||siz[y]>siz[son[x]])son[x]=y; } } int top[N]; void dfs2(int x,int f){ top[x]=f; if(son[x])dfs2(son[x],f); for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==son[x]||y==fa[x])continue; dfs2(y,y); } } int get_lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y]?x:y; }
這樣我們就有一個模板可以利用來求兩點之間的連線
所以get_dis
int get_dis(int x,int y){ return dep[x]+dep[y]-2*dep[get_lca(x,y)]; }
接下來就是關於我們去找重心然後更新權值的過程了
找重心並且將以重心為樹的連邊的重心樹建立,就是newfa[];
(重點是,每次的siz都要重新找,因為你的樹已經重構了,原來的siz已經不適用了)
int rt,alsiz,ms[N],mx; bool vis[N]; void get_rt(int x,int f){ siz[x]=1;ms[x]=0; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f||vis[y])continue; get_rt(y,x); siz[x]+=siz[y]; ms[x]=max(ms[x],siz[y]); } ms[x]=max(ms[x],alsiz-siz[x]); if(ms[x]<mx)mx=ms[x],rt=x; } int newfa[N]; void get_siz(int x,int f){ siz[x]=1; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f||vis[y])continue; get_siz(y,x); siz[x]+=siz[y]; } } void pre_dfs(int x){ vis[x]=1;get_siz(x,0); int tmp=siz[x]+5; t1[x].init(tmp);t2[x].init(tmp); for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(vis[y])continue; alsiz=mx=siz[y];get_rt(y,x); newfa[rt]=x;pre_dfs(rt); } }
接下來就是計算過程,我們要根據題意來計算,每一次更新之後的答案
這裡運用樹狀陣列來維護,當然線段樹也可以,不過我覺得我的碼量已經夠大了,就別禍害我的編譯器了
(這裡資料範圍過大,陣列開不出,用vector,但是記住,在用他的時候,一定要先插入零)
tr[i].resize(x);
或者是我在下面程式碼寫的那種,上面的是我在看題解的時候看到的,好像比我直接插入快好多~~~!!!
struct bit_tree{ vector<int> tr; int trsiz; void init(int x){ trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0); } void add(int x,int v){ x=min(x+1,trsiz); for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" "; //cout<<endl; } int query(int x){ x=min(x+1,trsiz);int ret=0; for(re i=x;i;i-=(i&(-i)))ret+=tr[i]; return ret; } }t1[N],t2[N];
然後就剩下最後的更新和查詢了
這個題要更新的是每個點的權值,而且是覆蓋式更新,所以我們要先做差,再更新
然後,為什麼可以用樹狀陣列呢?
每一次我們都以節點之間的距離為下標,更新這個點的樹狀陣列;
查詢的時候,直接根據限制的距離,在每一個樹狀陣列中查詢
記得容斥原理,定義兩個樹狀陣列,然後加爹的,減兒子的(這裡指的是重心樹中的父子關係)
void get_up(int x,int v){ for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v); //cout<<"sb"<<endl; for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v); } int get_ans(int x,int len){ int ret=t1[x].query(len); for(re i=x;newfa[i];i=newfa[i]){ int tmp=get_dis(newfa[i],x); if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp); } return ret; }
完整程式碼
#include<bits/stdc++.h> using namespace std; #define re register int const int N=100005; int n,m; int val[N]; struct bit_tree{ vector<int> tr; int trsiz; void init(int x){ trsiz=x+1;for(re i=0;i<=trsiz;i++)tr.push_back(0); } void add(int x,int v){ x=min(x+1,trsiz); for(re i=x;i<=trsiz;i+=(i&(-i)))tr[i]+=v;//cout<<i<<" "; //cout<<endl; } int query(int x){ x=min(x+1,trsiz);int ret=0; for(re i=x;i;i-=(i&(-i)))ret+=tr[i]; return ret; } }t1[N],t2[N]; int to[N<<1],nxt[N<<1],head[N<<1],rp; void add_edg(int x,int y){ to[++rp]=y; nxt[rp]=head[x]; head[x]=rp; } int siz[N],dep[N],son[N],fa[N]; void dfs1(int x,int f){ siz[x]=1; dep[x]=dep[f]+1; fa[x]=f; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f)continue; dfs1(y,x); siz[x]+=siz[y]; if(!son[x]||siz[y]>siz[son[x]])son[x]=y; } } int top[N]; void dfs2(int x,int f){ top[x]=f; if(son[x])dfs2(son[x],f); for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==son[x]||y==fa[x])continue; dfs2(y,y); } } int get_lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); x=fa[top[x]]; } return dep[x]<dep[y]?x:y; } int get_dis(int x,int y){ return dep[x]+dep[y]-2*dep[get_lca(x,y)]; } int rt,alsiz,ms[N],mx; bool vis[N]; void get_rt(int x,int f){ siz[x]=1;ms[x]=0; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f||vis[y])continue; get_rt(y,x); siz[x]+=siz[y]; ms[x]=max(ms[x],siz[y]); } ms[x]=max(ms[x],alsiz-siz[x]); if(ms[x]<mx)mx=ms[x],rt=x; } int newfa[N]; void get_siz(int x,int f){ siz[x]=1; for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(y==f||vis[y])continue; get_siz(y,x); siz[x]+=siz[y]; } } void pre_dfs(int x){ vis[x]=1;get_siz(x,0); int tmp=siz[x]+5; t1[x].init(tmp);t2[x].init(tmp); for(re i=head[x];i;i=nxt[i]){ int y=to[i]; if(vis[y])continue; alsiz=mx=siz[y];get_rt(y,x); newfa[rt]=x;pre_dfs(rt); } } void get_up(int x,int v){ for(re i=x;i;i=newfa[i])t1[i].add(get_dis(i,x),v); for(re i=x;newfa[i];i=newfa[i])t2[i].add(get_dis(newfa[i],x),v); } int get_ans(int x,int len){ int ret=t1[x].query(len); for(re i=x;newfa[i];i=newfa[i]){ int tmp=get_dis(newfa[i],x); if(len>=tmp)ret+=t1[newfa[i]].query(len-tmp)-t2[i].query(len-tmp); } return ret; } signed main(){ scanf("%d%d",&n,&m); for(re i=1;i<=n;i++)scanf("%d",&val[i]); for(re i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); add_edg(x,y);add_edg(y,x); } dfs1(1,0); dfs2(1,1); alsiz=mx=n; get_rt(1,0); pre_dfs(rt); for(re i=1;i<=n;i++)get_up(i,val[i]); int ans=0; for(re i=1;i<=m;i++){ int typ,x,y;scanf("%d%d%d",&typ,&x,&y); x^=ans;y^=ans; if(typ)get_up(x,y-val[x]),val[x]=y; else printf("%d\n",ans=get_ans(x,y)); } }