动态点分治复习笔记

动态点分治

学习笔记

总:个人感觉动态点分和点分几乎不是一个难度的啊~。动态点分的题更好,也更难,不像我做的那几道点分那么无聊。

  当然,动态点分治从题型上来看就是本来一个静态很好求的东西它一会改个点权什么的。于是它就动态了。

  说到底动态点分治还是和点分治还是有一定的联系的。至于我说的点分标准套路函数它应该除了$solve()$没有了之外,其他还都有保留。功能大概有一些区别。

  但其实动态点分也很套路,它也有几个非常非常套路的函数与非常非常套路的解题步骤。只是题灵活的多。

动态点分常用函数:

  1.$find-rt(int;x,int;fa)$ 和点分的同名函数一个作用,找树重心。

  2.$solve$_$work$_$tree(int;u)$写法和作用类似点分中的$work()$,但它主要是为了建出点分树。

  3.$dfs(int;u,int;fa)$ 也是为了统计最初的答案,同时最主要的是为了求出每个点到它在点分树上各级父亲的距离。

  4.$change(int;x,int;y)$ 当然从这个开始并不是一定有的了,但是大多数改点权的可能会存在这个函数,用来把$x$的权值改为$y$。

  5.$query(int;x)$ 用来求以$x$为根的时候求的一些问题。当然,具体情况具体分析。

动态点分常用思路:

  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
",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
",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
");
185             else printf("%d
",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.
");
187             else printf("%d
",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
",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
",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 }
小清新数据结构
原文地址:https://www.cnblogs.com/lnsyphy/p/11122835.html