动态点分治复习笔记
动态点分治
学习笔记
总:个人感觉动态点分和点分几乎不是一个难度的啊~。动态点分的题更好,也更难,不像我做的那几道点分那么无聊。
当然,动态点分治从题型上来看就是本来一个静态很好求的东西它一会改个点权什么的。于是它就动态了。
说到底动态点分治还是和点分治还是有一定的联系的。至于我说的点分标准套路函数它应该除了$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\n",ans); 14 }
这个就是查询操作了,看起来和修改也差不多。
每次要减去自己这棵子树内对父亲子树的影响,避免同一棵子树内的修改操作影响查询很多次。
那么这道题就完美解决了,大家有没有学会动态点分呢?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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——双堆。
每次想要删除就在删除堆中插入,查询时如果发现两堆堆顶相同就同时弹掉直到不同或弹空。
由于我当时刚学所以代码十分混乱还请见谅!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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$)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
P3345 [ZJOI2015]幻想乡战略游戏
题意:
求一棵带点权边权的树上的带权重心,以及重心的重量。
题解:
首先我们要会给定点求值,这一步需要动态点分。
日常建点分树,然后$dfs$出到各级父亲的距离。
对于每个点记录点权和$Sum[x]$,本棵子树内的带权重量$All[x]$,以及对于父亲的贡献$Up[x]$。
然后查询单点的值的时候从下到上枚举父亲,然后父亲代表的那棵子树中包括自己所在小子树和其他部分。
自己所在的小子树的贡献已经算完了,所以只需要计算其他部分就可以。
那么发现其他部分的值可以先用$All[fa]-Up[son]$计算出最初根的,在加上由于换根导致的$dist[st][cnt]*(Sum[fa]-Sum[son])$这样就统计完了。
然后询问重心的时候就是一个在树上二分(若干分)的过程。每次看往哪个儿子走更优就往那棵子树走。有点像快递员这道题的感觉
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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)$
无论根是啥都是一个定值。
于是就结束了。只要在最开始的根算出上述那堆东西然后就可以再套幻想乡战略游戏就行了。
真心好题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }