動態點分治複習筆記

動態點分治

學習筆記

總:我的感受動態點分和點分幾乎不是一個難度的啊~。動態點分的題更好,也更難,不像我作的那幾道點分那麼無聊。html

  固然,動態點分治從題型上來看就是原本一個靜態很好求的東西它一會改個點權什麼的。因而它就動態了。node

  說到底動態點分治仍是和點分治仍是有必定的聯繫的。至於我說的點分標準套路函數它應該除了$solve()$沒有了以外,其餘還都有保留。功能大概有一些區別。ios

  但其實動態點分也很套路,它也有幾個很是很是套路的函數與很是很是套路的解題步驟。只是題靈活的多。數組

動態點分經常使用函數:數據結構

  1.$find-rt(int\;x,int\;fa)$ 和點分的同名函數一個做用,找樹重心。app

  2.$solve$_$work$_$tree(int\;u)$寫法和做用相似點分中的$work()$,但它主要是爲了建出點分樹。ide

  3.$dfs(int\;u,int\;fa)$ 也是爲了統計最初的答案,同時最主要的是爲了求出每一個點到它在點分樹上各級父親的距離。函數

  4.$change(int\;x,int\;y)$ 固然從這個開始並非必定有的了,可是大多數改點權的可能會存在這個函數,用來把$x$的權值改成$y$。學習

  5.$query(int\;x)$ 用來求以$x$爲根的時候求的一些問題。固然,具體狀況具體分析。ui

動態點分經常使用思路:

  1.標準套路,先建出點分樹,而後要計算最初的時候的權值,以及每一個點分樹上的點表明的那棵子樹對於點分樹上的父親的貢獻。而且要求出到各級點分樹父親的距離。

  補:點分樹:還記得咱們點分時寫的那個$work()$函數嗎?只要把重心按深度相連就會變成一棵點分樹了。

        點分樹上的點並不必定是按原樹的深度排的深度,徹底有可能一個點在點分樹上的父親

        在原樹中的深度大於它。

        注意點分樹上每個點記錄的值表明原樹一棵子樹

  2.而後就能夠處理修改了,具體作法是從這個點開始往上找它點分樹上的父親。每次到一個點一般都會改兩個值,一個是本點的權值,一個是子樹對於點分樹父親的貢獻。

  3.詢問也是相似,只須要不停跳父親,而後每次加上大重心所表明的子樹中除小重心所表明的子樹外其它點對該點的貢獻並減去它子樹對於父親的貢獻。貌似有點抽象,看題吧。

動態點分解決問題:

  每次有一棵樹,而後動態改點權或點的狀態,而後求以不一樣點爲根時的答案或其它的。總之跟樹上的距離有關或跟樹鏈有關。

話很少說,咱們經過幾道例題來具體講解:

例題

BZOJ4372 爍爍的遊戲

題意:

給一顆n個節點的樹,邊權均爲1,初始點權均爲0,m次操做:
Q x:詢問x的點權。
M x d w:將樹上與節點x距離不超過d的節點的點權均加上w。

題解:

這是我最開始學動態點分時井然學長留的兩道題之一,另外一道題和它如出一轍。表示十分不滿。

這是一道動態點分比較板子的一道題。動態點分可能配合一個數據結構的,這道題配合的是線段樹。

下面咱們來分開每一個函數看看動態點分的套路。

 1 void find_rt(int u,int fa){
 2     wgt[u]=1;f[u]=0;
 3     int t=indexx[u],vv;
 4     while(t){
 5         vv=edge[t].v;
 6         if(!vist[vv] && vv!=fa){
 7             find_rt(vv,u);
 8             wgt[u]+=wgt[vv];
 9             f[u]=max(f[u],wgt[vv]);
10         }
11         t=edge[t].nxt;
12     }
13     f[u]=max(f[u],sum-wgt[u]);
14     if(f[u]<f[Root]) Root=u;
15 }

找重心標準操做,再也不多說了。

 1 void build_work_tree(int u){
 2     vist[u]=1;
 3     dfs(u,0,0);
 4     int t=indexx[u],vv;
 5     while(t){
 6         vv=edge[t].v;
 7         if(!vist[vv]){
 8             Root=0;sum=wgt[vv];
 9             find_rt(vv,u);
10             fa[Root]=u;
11             build_work_tree(Root);
12         }
13         t=edge[t].nxt;
14     }
15 }

這個是建點分樹的過程,具體步驟相似點分中的$solve()$,只是須要記錄一下它在點分樹上的父親。

 1 void dfs(int u,int f,int dep){
 2     dist[u].push_back(dep);
 3     int t=indexx[u],vv;
 4     while(t){
 5         vv=edge[t].v;
 6         if(!vist[vv] && vv!=f){
 7             dfs(vv,u,dep+1);
 8         }
 9         t=edge[t].nxt;
10     }
11 }

這道題的$dfs$並無太多功能,大概是動態點分的基本操做,就是記錄$u$到它的每一級父親的距離。

具體過程就像棧同樣,先遍歷到它的是它的最高父親,而後是次級父親......最後是本身,也就是0級父親。

可是這個函數執行完以後跟咱們想要的0級父親,一級父親......是反的,因此咱們再用一個函數把它正過來。

1 void init(){
2     for(int i=1;i<=n;i++){
3         int len=dist[i].size();
4         for(int j=0;j<len;j++){
5             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
6             else break;
7         }
8     }
9 }

這樣更方便在點分樹上跳的同時計算距離。

而後就要到修改操做與查詢操做了,中間咱們先插入一些線段樹的函數,方便一會研究修改。

 1 void push_up(int now){
 2     int l=tree[now].lson;
 3     int r=tree[now].rson;
 4     tree[now].sum=tree[l].sum+tree[r].sum;
 5 }
 6 void add(int &now,int p,int x,int l,int r){
 7     if(!now) now=++siz;
 8     if(l==r){
 9         tree[now].sum+=x;
10         return;
11     }
12     int mid=(l+r)/2;
13     if(p<=mid) add(tree[now].lson,p,x,l,mid);
14     else add(tree[now].rson,p,x,mid+1,r);
15     push_up(now);
16 }
17 int ask(int now,int ql,int qr,int l,int r){
18     if(!now) return 0;
19     if(ql<=l && r<=qr){
20         return tree[now].sum;
21     }
22     int mid=(l+r)/2,ret=0;
23     if(ql<=mid) ret+=ask(tree[now].lson,ql,qr,l,mid);
24     if(qr>mid) ret+=ask(tree[now].rson,ql,qr,mid+1,r);
25     return ret;
26 }

動態開點線段樹,$ask()$區間求和,$add()$是單點修改。注意線段樹數組必定要開很大很大。

而後是重點了。

 1 void Add(int x,int y,int z){
 2     int st=x,cnt=0,temp;
 3     while(x){
 4         temp=dist[st][cnt];
 5         if(temp<=y) add(root[x][0],y-temp,z,0,n);
 6         if(fa[x]){
 7             temp=dist[st][cnt+1];
 8             if(temp<=y) add(root[x][1],y-temp,z,0,n);
 9         }
10         x=fa[x];cnt++;
11     }
12 }

這個表明原題目中的吸引皮皮鼠。動態點分比較標準的修改操做之一。

動態點分的修改一般都是從本身開始向上跳父親,每次都要記錄本身這棵子樹內的點對於父親子樹的貢獻。

具體爲何你們畫畫圖理解一下,一會到查詢可能看起來還能好點。

到了這道題就是表明在這個大重心的距離爲$y-temp$之內的點可能受到影響,因爲單點修改區間查詢比較好寫,我就沒寫區間修改。

 1 void Query(int x){
 2     int st=x,cnt=0,temp;
 3     ans=0;
 4     while(x){
 5         temp=dist[st][cnt];
 6         ans+=ask(root[x][0],temp,n,0,n);
 7         if(fa[x]){
 8             temp=dist[st][cnt+1];
 9             ans-=ask(root[x][1],temp,n,0,n);
10         }
11         x=fa[x],cnt++;
12     }
13     printf("%d\n",ans);
14 }

這個就是查詢操做了,看起來和修改也差很少。

每次要減去本身這棵子樹內對父親子樹的影響,避免同一棵子樹內的修改操做影響查詢不少次。

那麼這道題就完美解決了,你們有沒有學會動態點分呢?

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <vector>
  5 #define N 200011
  6 #define INF 0x7f7f7f7f
  7 using namespace std;
  8 struct apple{
  9     int v,nxt;
 10 }edge[N*4];
 11 struct node{
 12     int lson,rson,sum;
 13 }tree[12800011*2];
 14 vector<int> dist[N];
 15 int indexx[N],tot,ans,siz,wgt[N],f[N],Root,root[N][2],sum,n,m,vist[N];
 16 int fa[N];
 17 char s[111];
 18 void addedge(int x,int y){
 19     edge[++tot].v=y;
 20     edge[tot].nxt=indexx[x];
 21     indexx[x]=tot;
 22 }
 23 void push_up(int now){
 24     int l=tree[now].lson;
 25     int r=tree[now].rson;
 26     tree[now].sum=tree[l].sum+tree[r].sum;
 27 }
 28 void add(int &now,int p,int x,int l,int r){
 29     if(!now) now=++siz;
 30     if(l==r){
 31         tree[now].sum+=x;
 32         return;
 33     }
 34     int mid=(l+r)/2;
 35     if(p<=mid) add(tree[now].lson,p,x,l,mid);
 36     else add(tree[now].rson,p,x,mid+1,r);
 37     push_up(now);
 38 }
 39 int ask(int now,int ql,int qr,int l,int r){
 40     if(!now) return 0;
 41     if(ql<=l && r<=qr){
 42         return tree[now].sum;
 43     }
 44     int mid=(l+r)/2,ret=0;
 45     if(ql<=mid) ret+=ask(tree[now].lson,ql,qr,l,mid);
 46     if(qr>mid) ret+=ask(tree[now].rson,ql,qr,mid+1,r);
 47     return ret;
 48 }
 49 void find_rt(int u,int fa){
 50     wgt[u]=1;f[u]=0;
 51     int t=indexx[u],vv;
 52     while(t){
 53         vv=edge[t].v;
 54         if(!vist[vv] && vv!=fa){
 55             find_rt(vv,u);
 56             wgt[u]+=wgt[vv];
 57             f[u]=max(f[u],wgt[vv]);
 58         }
 59         t=edge[t].nxt;
 60     }
 61     f[u]=max(f[u],sum-wgt[u]);
 62     if(f[u]<f[Root]) Root=u;
 63 }
 64 void dfs(int u,int f,int dep){
 65     dist[u].push_back(dep);
 66     int t=indexx[u],vv;
 67     while(t){
 68         vv=edge[t].v;
 69         if(!vist[vv] && vv!=f){
 70             dfs(vv,u,dep+1);
 71         }
 72         t=edge[t].nxt;
 73     }
 74 }
 75 void build_work_tree(int u){
 76     vist[u]=1;
 77     dfs(u,0,0);
 78     int t=indexx[u],vv;
 79     while(t){
 80         vv=edge[t].v;
 81         if(!vist[vv]){
 82             Root=0;sum=wgt[vv];
 83             find_rt(vv,u);
 84             fa[Root]=u;
 85             build_work_tree(Root);
 86         }
 87         t=edge[t].nxt;
 88     }
 89 }
 90 void Add(int x,int y,int z){
 91     int st=x,cnt=0,temp;
 92     while(x){
 93         temp=dist[st][cnt];
 94         if(temp<=y) add(root[x][0],y-temp,z,0,n);
 95         if(fa[x]){
 96             temp=dist[st][cnt+1];
 97             if(temp<=y) add(root[x][1],y-temp,z,0,n);
 98         }
 99         x=fa[x];cnt++;
100     }
101 }
102 void Query(int x){
103     int st=x,cnt=0,temp;
104     ans=0;
105     while(x){
106         temp=dist[st][cnt];
107         ans+=ask(root[x][0],temp,n,0,n);
108         if(fa[x]){
109             temp=dist[st][cnt+1];
110             ans-=ask(root[x][1],temp,n,0,n);
111         }
112         x=fa[x],cnt++;
113     }
114     printf("%d\n",ans);
115 }
116 void init(){
117     for(int i=1;i<=n;i++){
118         int len=dist[i].size();
119         for(int j=0;j<len;j++){
120             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
121             else break;
122         }
123     }
124 }
125 int main(){
126     freopen("a.txt","r",stdin);
127     int x,y,z;
128     scanf("%d%d",&n,&m);
129     for(int i=1;i<n;i++){
130         scanf("%d%d",&x,&y);
131         addedge(x,y);
132         addedge(y,x);
133     }
134     f[0]=INF;sum=n;Root=0;
135     find_rt(1,0);
136     build_work_tree(Root);
137     init();
138     for(int i=1;i<=m;i++){
139         scanf("%s",s);
140         if(s[0]=='M'){
141             scanf("%d%d%d",&x,&y,&z);
142             Add(x,y,z);
143         }
144         else{
145             scanf("%d",&x);
146             Query(x);
147         }
148     }
149     return 0;
150 }
爍爍的遊戲

P2056 [ZJOI2007]捉迷藏

題意:

也許直接看Qtree4的題面描述會更好些。大概就是給一棵樹,最初樹上的點都是黑點,有若干操做能夠改變點的狀態,問任意兩個黑點之間的最大距離是多少。

題解:

動態點分不錯的題啊,也是我作的第二道動態點分。

想到兩個黑點之間的最大距離必定是能夠由兩條鏈拼起來,因此咱們在每一個點維護鏈的集合,每次取最長+次長就行了。

考慮動態點分,建出點分樹。

因爲咱們應該避免最長和次長出如今同一棵子樹內,因此咱們只把該子樹內的最長鏈貢獻給父親。

注意本棵子樹內到小重心的最長鏈不必定是到大重心的最長鏈!因此應該分開維護。

這樣的話若是是修改就從本身開始在維護的小子樹集合中插入或刪除,並修改對於大子樹的貢獻。

採用一個很強的操做避免掉set——雙堆。

每次想要刪除就在刪除堆中插入,查詢時若是發現兩堆堆頂相同就同時彈掉直到不一樣或彈空。

因爲我當時剛學因此代碼十分混亂還請見諒!

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <queue>
  5 #include <vector>
  6 #define INF 2000000000
  7 #define N 200011
  8 using namespace std;
  9 struct apple{
 10     int v,nxt;
 11 }edge[N*4];
 12 vector<int> dist[N];
 13 int vist[N],f[N],siz[N],Anum[N],Bnum[N],sum,rt,fa[N],ans,indexx[N],tot,n,m,dp[N],sit[N];
 14 priority_queue<int> A[N],B[N],D[N],E[N],C;
 15 char s[111];
 16 inline int read(){
 17     int f=1,ret=0;char ch=getchar();
 18     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 19     while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
 20     return ret;
 21 }
 22 void addedge(int x,int y){
 23     edge[++tot].v=y;
 24     edge[tot].nxt=indexx[x];
 25     indexx[x]=tot;
 26 }
 27 void find_rt(int u,int ff){
 28     int t,vv;
 29     t=indexx[u];
 30     siz[u]=1;f[u]=0;
 31     while(t){
 32         vv=edge[t].v;
 33         if(!vist[vv] && vv!=ff){
 34             find_rt(vv,u);
 35             siz[u]+=siz[vv];
 36             f[u]=max(f[u],siz[vv]);
 37         }
 38         t=edge[t].nxt;
 39     }
 40     f[u]=max(f[u],sum-siz[u]);
 41     if(f[u]<f[rt]) rt=u;
 42 }
 43 void basic_dfs(int anc,int top,int u,int f,int dep){
 44     int t,vv;
 45     dist[u].push_back(dep);
 46     B[top].push(dep);
 47     Bnum[top]++;
 48     t=indexx[u];
 49     while(t){
 50         vv=edge[t].v;
 51         if(!vist[vv] && vv!=f){
 52             basic_dfs(anc,top,vv,u,dep+1);
 53         }
 54         t=edge[t].nxt;
 55     }
 56 }
 57 int get_max(int u,int op){
 58     if(op==1){
 59         while(!D[u].empty() && !A[u].empty() && D[u].top()==A[u].top()){
 60             D[u].pop();A[u].pop();
 61         }
 62         if(!A[u].empty()) return A[u].top();
 63         else return -INF;
 64     }
 65     else{
 66         while(!E[u].empty() && !B[u].empty() && B[u].top()==E[u].top()){
 67             B[u].pop();E[u].pop();
 68         }
 69         if(!B[u].empty()) return B[u].top();
 70         else return -INF;
 71     }
 72 }
 73 int get_second(int u,int op){
 74     int ret;
 75     if(op==1){
 76         int t=A[u].top();A[u].pop();
 77         ret=get_max(u,1);
 78         A[u].push(t);
 79         return ret;
 80     }
 81     else{
 82         int t=B[u].top();B[u].pop();
 83         ret=get_max(u,2);
 84         B[u].push(t);
 85         return ret;
 86     }
 87 }
 88 void build_work_tree(int u){
 89     vist[u]=1;
 90     A[u].push(0);
 91     Anum[u]++;
 92     dp[u]=0;
 93     int t,vv;
 94     t=indexx[u];
 95     while(t){
 96         vv=edge[t].v;
 97         if(!vist[vv]){
 98             sum=siz[vv];rt=0;
 99             find_rt(vv,u);
100             fa[rt]=u;
101             basic_dfs(u,rt,vv,0,1);
102             if(!A[u].empty()) dp[u]=max(dp[u],get_max(u,1)+get_max(rt,2));
103             A[u].push(B[rt].top());Anum[u]++;
104             build_work_tree(rt);
105         }
106         t=edge[t].nxt;
107     }
108     A[n+1].push(dp[u]);
109 }
110 void solve(int x){
111     sit[x]^=1;
112     D[n+1].push(dp[x]);
113     if(sit[x]){
114         A[x].push(0);
115         Anum[x]++;
116     }
117     else{
118         D[x].push(0);
119         Anum[x]--;
120     }
121     if(Anum[x]==0 || get_max(x,1)<0) dp[x]=-INF;
122     else if(Anum[x]==1 || get_second(x,1)<0) dp[x]=0;
123     else dp[x]=get_max(x,1)+get_second(x,1);
124     A[n+1].push(dp[x]);
125     int u=x,t=0;
126     while(fa[u]){
127         int f=fa[u];
128         if(Bnum[u]==0) D[fa[u]].push(-INF);
129         else if(Bnum[u]==1) D[fa[u]].push(get_max(u,2));
130         else D[fa[u]].push(get_max(u,2));
131         if(sit[x]){
132             B[u].push(dist[x][t]);
133             Bnum[u]++;
134         }
135         else{
136             E[u].push(dist[x][t]);
137             Bnum[u]--;
138         }
139         if(Bnum[u]==0) A[fa[u]].push(-INF);
140         else if(Bnum[u]==1) A[fa[u]].push(get_max(u,2));
141         else A[fa[u]].push(get_max(u,2));
142         D[n+1].push(dp[fa[u]]);
143         if(get_max(fa[u],1)<0) dp[fa[u]]=-INF;
144         else if(get_second(fa[u],1)<0) dp[fa[u]]=0;
145         else dp[fa[u]]=get_max(fa[u],1)+get_second(fa[u],1);
146         A[n+1].push(dp[fa[u]]);
147         u=fa[u];t++;
148     }
149 }
150 int query(int x){
151     return get_max(n+1,1);
152 }
153 int main(){
154     freopen("a.txt","r",stdin);
155     int x,y,z;
156     n=read();
157     for(int i=1;i<n;i++){
158         x=read();
159         y=read();
160         addedge(x,y);
161         addedge(y,x);
162         sit[i]=1;
163     }
164     sit[n]=1;
165     rt=0;f[0]=INF;sum=n;
166     find_rt(1,0);
167     build_work_tree(rt);
168     for(int i=1;i<=n;i++){
169         int len=dist[i].size();
170         for(int j=0;j<len;j++){
171             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
172             else break;
173         }
174     }
175     scanf("%d",&m);
176     for(int i=1;i<=m;i++){
177         scanf("%s",s);
178         if(s[0]=='C'){
179             x=read();
180             solve(x);
181         }
182         else{
183             ans=query(x);
184             if(ans<0) printf("-1\n");
185             else printf("%d\n",ans);
186         }
187     }
188     return 0;
189 }
捉迷藏

P4115 Qtree4

題意:

給出一棵邊帶權的節點數量爲n的樹,初始樹上全部節點都是白色。有兩種操做:

C x,改變節點x的顏色,即白變黑,黑變白

A,詢問樹中最遠的兩個白色節點的距離,這兩個白色節點能夠重合(此時距離爲0)。

題解:

和捉迷藏幾乎就是同樣的,多增長了邊權,有一些小細節須要修改。(好比最小值不能設0而應該是$-INF$)。

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <queue>
  5 #include <vector>
  6 #define INF 0x7fffffff
  7 #define N 100011
  8 using namespace std;
  9 struct apple{
 10     int v,nxt,q;
 11 }edge[N*2];
 12 vector<int> dist[N];
 13 int vist[N],f[N],siz[N],Anum[N],Bnum[N],sum,rt,fa[N],ans,indexx[N],tot,n,m,dp[N],sit[N];
 14 priority_queue<int> A[N],B[N],D[N],E[N];
 15 char s[111];
 16 inline int read(){
 17     int f=1,ret=0;char ch=getchar();
 18     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 19     while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
 20     return ret*f;
 21 }
 22 void addedge(int x,int y,int z){
 23     edge[++tot].v=y;
 24     edge[tot].q=z;
 25     edge[tot].nxt=indexx[x];
 26     indexx[x]=tot;
 27 }
 28 void find_rt(int u,int ff){
 29     int t,vv;
 30     t=indexx[u];
 31     siz[u]=1;f[u]=0;
 32     while(t){
 33         vv=edge[t].v;
 34         if(!vist[vv] && vv!=ff){
 35             find_rt(vv,u);
 36             siz[u]+=siz[vv];
 37             f[u]=max(f[u],siz[vv]);
 38         }
 39         t=edge[t].nxt;
 40     }
 41     f[u]=max(f[u],sum-siz[u]);
 42     if(f[u]<f[rt]) rt=u;
 43 }
 44 void basic_dfs(int anc,int top,int u,int f,int dep){
 45     int t,vv;
 46     dist[u].push_back(dep);
 47     B[top].push(dep);
 48     Bnum[top]++;
 49     t=indexx[u];
 50     while(t){
 51         vv=edge[t].v;
 52         if(!vist[vv] && vv!=f){
 53             basic_dfs(anc,top,vv,u,dep+edge[t].q);
 54         }
 55         t=edge[t].nxt;
 56     }
 57 }
 58 inline int get_max(int u,int op){
 59     if(op==1){
 60         while(!D[u].empty() && !A[u].empty() && D[u].top()==A[u].top()){
 61             D[u].pop();A[u].pop();
 62         }
 63         if(!A[u].empty()) return A[u].top();
 64         else return -INF;
 65     }
 66     else{
 67         while(!E[u].empty() && !B[u].empty() && B[u].top()==E[u].top()){
 68             B[u].pop();E[u].pop();
 69         }
 70         if(!B[u].empty()) return B[u].top();
 71         else return -INF;
 72     }
 73 }
 74 inline int get_second(int u,int op){
 75     int ret;
 76     if(op==1){
 77         int t=A[u].top();A[u].pop();
 78         ret=get_max(u,1);
 79         A[u].push(t);
 80         return ret;
 81     }
 82     else{
 83         int t=B[u].top();B[u].pop();
 84         ret=get_max(u,2);
 85         B[u].push(t);
 86         return ret;
 87     }
 88 }
 89 void build_work_tree(int u){
 90     vist[u]=1;
 91     A[u].push(0);
 92     Anum[u]++;
 93     dp[u]=-INF;
 94     int t,vv;
 95     t=indexx[u];
 96     while(t){
 97         vv=edge[t].v;
 98         if(!vist[vv]){
 99             sum=siz[vv];rt=0;
100             find_rt(vv,u);
101             fa[rt]=u;
102             basic_dfs(u,rt,vv,0,edge[t].q);
103             if(!A[u].empty()) dp[u]=max(dp[u],get_max(u,1)+get_max(rt,2));
104             A[u].push(B[rt].top());Anum[u]++;
105             build_work_tree(rt);
106         }
107         t=edge[t].nxt;
108     }
109     A[n+1].push(dp[u]);
110 }
111 void solve(int x){
112     sit[x]^=1;
113     D[n+1].push(dp[x]);
114     if(sit[x]){
115         A[x].push(0);
116         Anum[x]++;
117     }
118     else{
119         D[x].push(0);
120         Anum[x]--;
121     }
122     if(Anum[x]==0 || get_max(x,1)<-10000000) dp[x]=-INF;
123     else if(Anum[x]==1 || get_second(x,1)<-10000000) dp[x]=0;
124     else dp[x]=get_max(x,1)+get_second(x,1);
125     A[n+1].push(dp[x]);
126     int u=x,t=0;
127     while(fa[u]){
128         int f=fa[u];
129         if(Bnum[u]==0) D[fa[u]].push(-INF);
130         else if(Bnum[u]==1) D[fa[u]].push(get_max(u,2));
131         else D[fa[u]].push(get_max(u,2));
132         if(sit[x]){
133             B[u].push(dist[x][t]);
134             Bnum[u]++;
135         }
136         else{
137             E[u].push(dist[x][t]);
138             Bnum[u]--;
139         }
140         if(Bnum[u]==0) A[fa[u]].push(-INF);
141         else if(Bnum[u]==1) A[fa[u]].push(get_max(u,2));
142         else A[fa[u]].push(get_max(u,2));
143         D[n+1].push(dp[fa[u]]);
144         if(get_max(fa[u],1)<-10000000) dp[fa[u]]=-INF;
145         else if(get_second(fa[u],1)<-10000000) dp[fa[u]]=0;
146         else dp[fa[u]]=get_max(fa[u],1)+get_second(fa[u],1);
147         A[n+1].push(dp[fa[u]]);
148         u=fa[u];t++;
149     }
150 }
151 int query(int x){
152     return get_max(n+1,1);
153 }
154 signed main(){
155 //    freopen("a.txt","r",stdin);
156     int x,y,z;
157     n=read();
158     for(int i=1;i<n;i++){
159         x=read();
160         y=read();
161         z=read();
162         addedge(x,y,z);
163         addedge(y,x,z);
164         sit[i]=1;
165     }
166     sit[n]=1;
167     rt=0;f[0]=INF;sum=n;
168     find_rt(1,0);
169     build_work_tree(rt);
170     for(int i=1;i<=n;i++){
171         int len=dist[i].size();
172         for(int j=0;j<len;j++){
173             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
174             else break;
175         }
176     }
177     scanf("%d",&m);
178     for(int i=1;i<=m;++i){
179         scanf("%s",s);
180         if(s[0]=='C'){
181             x=read();
182             solve(x);
183         }
184         else{
185             ans=query(x);
186             if(ans<-10000000) printf("They have disappeared.\n");
187             else printf("%d\n",ans);
188         }
189     }
190     return 0;
191 }
Qtree4

P3345 [ZJOI2015]幻想鄉戰略遊戲

題意:

求一棵帶點權邊權的樹上的帶權重心,以及重心的重量。

題解:

首先咱們要會給定點求值,這一步須要動態點分。

平常建點分樹,而後$dfs$出到各級父親的距離。

對於每一個點記錄點權和$Sum[x]$,本棵子樹內的帶權重量$All[x]$,以及對於父親的貢獻$Up[x]$。

而後查詢單點的值的時候從下到上枚舉父親,而後父親表明的那棵子樹中包括本身所在小子樹和其餘部分。

本身所在的小子樹的貢獻已經算完了,因此只須要計算其餘部分就能夠。

那麼發現其餘部分的值能夠先用$All[fa]-Up[son]$計算出最初根的,在加上因爲換根致使的$dist[st][cnt]*(Sum[fa]-Sum[son])$這樣就統計完了。

而後詢問重心的時候就是一個在樹上二分(若干分)的過程。每次看往哪一個兒子走更優就往那棵子樹走。有點像快遞員這道題的感受

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <vector>
  6 #define N 200011
  7 #define INF 0x3f3f3f3f
  8 #define int long long
  9 using namespace std;
 10 struct apple{
 11     int v,q,nxt,root;
 12 }edge[N*4];
 13 int indexx[N],vist[N],siz[N],f[N],sum,Sum[N],Up[N],All[N],rt,Root,n,m,tot,fa[N],ans;
 14 vector<int> dist[N];
 15 void addedge(int x,int y,int z){
 16     edge[++tot].v=y;
 17     edge[tot].q=z;
 18     edge[tot].nxt=indexx[x];
 19     indexx[x]=tot;
 20 }
 21 void find_rt(int u,int fa){
 22     int t,vv;
 23     siz[u]=1;
 24     f[u]=0;
 25     t=indexx[u];
 26     while(t){
 27         vv=edge[t].v;
 28         if(vv!=fa && !vist[vv]){
 29             find_rt(vv,u);
 30             siz[u]+=siz[vv];
 31             f[u]=max(f[u],siz[vv]);
 32         }
 33         t=edge[t].nxt;
 34     }
 35     f[u]=max(f[u],sum-siz[u]);
 36     if(f[u]<f[rt]) rt=u;
 37 }
 38 void dfs(int u,int f,int dep){
 39     dist[u].push_back(dep);
 40     int t,vv,qq;
 41     t=indexx[u];
 42     while(t){
 43         vv=edge[t].v;
 44         qq=edge[t].q;
 45         if(!vist[vv] && vv!=f){
 46             dfs(vv,u,dep+qq);
 47         }
 48         t=edge[t].nxt;
 49     }
 50 }
 51 void build_work_tree(int u){
 52     dfs(u,0,0);
 53     int t,vv;
 54     vist[u]=1;
 55     t=indexx[u];
 56     while(t){
 57         vv=edge[t].v;
 58         if(!vist[vv]){
 59             rt=0;sum=siz[vv];
 60             find_rt(vv,0);
 61             edge[t].root=rt;
 62 //            no[rt]=++cnt;
 63             fa[rt]=u;
 64             build_work_tree(rt);
 65         }
 66         t=edge[t].nxt;
 67     }
 68 }
 69 void change(int x,int y){
 70     int u=x,cnt=1,vv=x;
 71     Sum[x]+=y;
 72     x=fa[x];
 73     while(x){
 74         Sum[x]+=y;
 75         Up[vv]+=y*dist[u][cnt];
 76         All[x]+=y*dist[u][cnt];
 77         vv=x;
 78         x=fa[x];
 79         cnt++;
 80     }
 81 }
 82 int ask(int x){
 83     int u=x,cnt=1,ret=0,vv=x;
 84     ret+=All[x];
 85     x=fa[x];
 86     while(x){
 87         ret+=(Sum[x]-Sum[vv])*dist[u][cnt]+All[x]-Up[vv];
 88         vv=x;
 89         x=fa[x];
 90         cnt++;
 91     }
 92     return ret;
 93 }
 94 void query(int x){
 95     vist[x]=1;
 96     int t,vv,minn,nxt;
 97     minn=ask(x);nxt=x;
 98     t=indexx[x];
 99     while(t){
100         vv=edge[t].v;
101         if(!vist[vv]){
102             int temp=ask(vv);
103             if(temp<minn){
104                 minn=temp;
105                 nxt=edge[t].root;
106             }
107         }
108         t=edge[t].nxt;
109     }
110     if(nxt==x){
111         ans=minn;
112         vist[x]=0;
113         return;
114     }
115     else query(nxt);
116     vist[x]=0;
117 }
118 signed main(){
119     freopen("a.txt","r",stdin);
120     int x,y,z;
121     scanf("%lld%lld",&n,&m);
122     for(int i=1;i<n;i++){
123         scanf("%lld%lld%lld",&x,&y,&z);
124         addedge(x,y,z);
125         addedge(y,x,z);
126     }
127     f[0]=INF;sum=n;
128     find_rt(1,0);
129     Root=rt;
130     build_work_tree(rt);
131     for(int i=1;i<=n;i++){
132         int len=dist[i].size();
133         for(int j=0;j<len;j++){
134             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
135             else break;
136         }
137     }
138     memset(vist,0,sizeof(vist));
139     for(int i=1;i<=m;i++){
140         scanf("%lld%lld",&x,&y);
141         change(x,y);
142         query(Root);
143         printf("%lld\n",ans);
144     }
145     return 0;
146 }
幻想鄉戰略遊戲

P3676 小清新數據結構題

題意:

給出一棵n個點的樹,每一個點有一個點權。

如今有q次操做,每次操做是修改一個點的點權或指定一個點,詢問以這個點爲根時每棵子樹點權和的平方和。

題解:

不愧是NOI rk1 ,zzq的題真的很棒。

若是查詢點權和的話很好搞,就變成了幻想鄉戰略遊戲。

而後發現有一個很小清新的結論(證實見zzq的洛谷題解吧):

 

$\sum_{i=1}^n s_i(w-s_i)$

不管根是啥都是一個定值。

因而就結束了。只要在最開始的根算出上述那堆東西而後就能夠再套幻想鄉戰略遊戲就好了。

真心好題。

  1 #include <iostream>
  2 #include <cstdlib>
  3 #include <cstdio>
  4 #include <vector>
  5 #define N 400011
  6 #define int long long
  7 #define INF 0x7f7f7f7f
  8 using namespace std;
  9 struct apple{
 10     int v,nxt;
 11 }edge[N*4];
 12 int indexx[N],tot,f[N],siz[N],sum,n,m,rt,Sum[N],sigma,All[N],Up[N],val[N],vist[N],fa[N],W,ans;
 13 vector<int> dist[N];
 14 void addedge(int x,int y){
 15     edge[++tot].v=y;
 16     edge[tot].nxt=indexx[x];
 17     indexx[x]=tot;
 18 }
 19 void find_rt(int u,int fa){
 20     int t,vv;
 21     siz[u]=1;
 22     f[u]=0;
 23     t=indexx[u];
 24     while(t){
 25         vv=edge[t].v;
 26         if(vv!=fa && !vist[vv]){
 27             find_rt(vv,u);
 28             siz[u]+=siz[vv];
 29             f[u]=max(f[u],siz[vv]);
 30         }
 31         t=edge[t].nxt;
 32     }
 33     f[u]=max(f[u],sum-siz[u]);
 34     if(f[u]<f[rt]) rt=u;
 35 }
 36 void dfs(int anc,int u,int fa,int dep,int op){
 37     if(op==0) dist[u].push_back(dep);
 38     if(op==0){
 39         All[anc]+=dep*val[u];
 40         Sum[anc]+=val[u];
 41     }
 42     else Up[anc]+=dist[u][dist[u].size()-1]*val[u];
 43     int t,vv;
 44     t=indexx[u];
 45     while(t){
 46         vv=edge[t].v;
 47         if(!vist[vv] && vv!=fa){
 48             dfs(anc,vv,u,dep+1,op);
 49         }
 50         t=edge[t].nxt;
 51     }
 52 }
 53 void work(int u){
 54     dfs(u,u,0,0,0);
 55     vist[u]=1;
 56     int t,vv;
 57     t=indexx[u];
 58     while(t){
 59         vv=edge[t].v;
 60         if(!vist[vv]){
 61             sum=siz[vv];rt=0;
 62             find_rt(vv,0);
 63             fa[rt]=u;
 64             dfs(rt,rt,0,0,1);
 65             work(rt);
 66         }
 67         t=edge[t].nxt;
 68     }
 69 }
 70 void DP(int u,int fa){
 71     int t,vv;
 72     siz[u]=val[u];
 73     t=indexx[u];
 74     while(t){
 75         vv=edge[t].v;
 76         if(vv!=fa){
 77             DP(vv,u);
 78             siz[u]+=siz[vv];
 79         }
 80         t=edge[t].nxt;
 81     }
 82     W+=siz[u]*(sigma-siz[u]);
 83 }
 84 int query(int x){
 85     int ret=0,u=x,temp,cnt=1;
 86     ret+=All[x];
 87     while(fa[x]){
 88         ret-=Up[x];
 89         ret+=All[fa[x]];
 90         ret+=(dist[u][cnt])*(Sum[fa[x]]-Sum[x]);
 91         x=fa[x];
 92         cnt++;
 93     }
 94     return ret;
 95 }
 96 void change(int x,int y){
 97     W-=val[x]*(query(x));
 98     int u=x,cnt=1;
 99     Sum[u]+=y-val[x];
100 //    All[u]+=y-val[x];
101     while(fa[x]){
102         Sum[fa[x]]+=y-val[u];
103         All[fa[x]]+=(y-val[u])*dist[u][cnt];
104         Up[x]+=(y-val[u])*dist[u][cnt];
105         cnt++;
106         x=fa[x];
107     }
108     sigma+=y-val[u];
109     val[u]=y;
110     W+=val[u]*(query(u));
111 }
112 void ask(int x){
113     ans=sigma*(query(x)+sigma)-W;
114     printf("%lld\n",ans);
115 }
116 signed main(){
117     freopen("a.txt","r",stdin);
118     int x,y,op;
119     scanf("%lld%lld",&n,&m);
120     for(int i=1;i<n;i++){
121         scanf("%lld%lld",&x,&y);
122         addedge(x,y);
123         addedge(y,x);
124     }
125     for(int i=1;i<=n;i++){
126         scanf("%lld",&val[i]);
127         sigma+=val[i];
128     }
129     rt=0;f[0]=INF;sum=n;
130     find_rt(1,0);
131     work(rt);
132     DP(1,0);
133     for(int i=1;i<=n;i++){
134         int len=dist[i].size();
135         for(int j=0;j<len;j++){
136             if(j<len-j-1) swap(dist[i][j],dist[i][len-j-1]);
137             else break;
138         }
139     }
140     for(int i=1;i<=m;i++){
141         scanf("%lld",&op);
142         if(op==1){
143             scanf("%lld%lld",&x,&y);
144             change(x,y);
145         }
146         else{
147             scanf("%lld",&x);
148             ask(x);
149         }
150     }
151     return 0;
152 }
小清新數據結構
相關文章
相關標籤/搜索