题目描述
给出一棵边带权的节点数量为n的树,初始树上所有节点都是白色。有两种操作:
C x,改变节点x的颜色,即白变黑,黑变白
A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0)。
N (N <= 100000) Q <= 200000
时限1s
题解
如果没有修改的话,直接点分治,记录子树最深的白点即可。
但是有修改。
发现,点分治的递归层数是O(logn)的
而且这个递归的联通块的根形成一个树形结构。不妨叫点分树。
我们的答案是在所有递归出来的块里ans取max
发现,每次改变一个点的颜色,会影响自己的联通块,以及点分树上这个点的所有father的答案。
树高logn
所以,我们考虑暴力修改每一层的答案。
用三个堆来维护。
一个堆h0,维护这个点P所代表的点分树的联通块中所有点到点分树上P的father的距离(树上实际距离)。
(好处是,修改的时候,直接自底向上,父亲只要一个,这样不需要在上层再考虑哪个子树变了)
另一个堆h1,维护这个点P的所有儿子的堆顶的值。
我们从一个点P的h1堆里面,找到最大的和次大的,做和就是这一层的最大答案。
如果P是白点,那么答案可以只要最大的。而且ans最少是0
第三个堆,维护所有点代表的联通块的ans。最终答案就在这里。
然后,所有的修改,都是删除之后再插入。
堆的删除,用懒惰堆即可。
代码:
(实现细节较多:例如堆的empty判断)
(堆中不用记录答案出自哪里,随便删除一个,剩下那个就是没有删除的。是没有区别的。直接int的堆即可)
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } il void prin(int x){ if(x/10) prin(x/10); putchar(x%10+'0'); } namespace Miracle{ const int N=100000+5; const int inf=0x3f3f3f3f; int n,m; struct node{ int nxt,to; int val; }e[2*N]; int hd[N],cnt; il void add(int x,int y,int z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].val=z; hd[x]=cnt; } int fa[N]; int sz[N]; int c[N]; priority_queue<int>h[2][N],d[2][N],hh,dd; int dis[N][20]; int dep[N]; int nowsz; int rt,mxsz[N]; int ans[N]; bool vis[N]; int gen; il void dfs1(int x,int ff,int d){ dep[x]=d; sz[x]=1; mxsz[x]=0; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==ff) continue; if(vis[y]) continue; dfs1(y,x,d); sz[x]+=sz[y]; mxsz[x]=max(mxsz[x],sz[y]); } mxsz[x]=max(mxsz[x],nowsz-sz[x]); if(mxsz[x]<=nowsz/2){ rt=x; } } il void dfs2(int x,int ff,int d){ sz[x]=1; if(d!=1) h[0][rt].push(dis[x][d-1]); for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]) continue; if(y==ff) continue; dis[y][d]=dis[x][d]+e[i].val; dfs2(y,x,d); sz[x]+=sz[y]; } } il void clear(int x,int k){ if(h[k][x].empty()) return; while(d[k][x].size()&&h[k][x].size()&&h[k][x].top()==d[k][x].top()){ h[k][x].pop();d[k][x].pop(); } } il void upda(int x){ //cout<<" updaing "<<x<<endl; ans[x]=-inf; if(c[x]==0) ans[x]=max(ans[x],0); clear(x,1); if(!h[1][x].empty()){ //cout<<" sz "<<h[1][x].size()<<" "<<d[1][x].size()<<endl; int tmp=h[1][x].top();h[1][x].pop(); //cout<<" tmp "<<tmp.id<<" "<<tmp.val<<endl; if(c[x]==0){ ans[x]=max(ans[x],tmp); } clear(x,1); if(!h[1][x].empty()){ ans[x]=max(ans[x],tmp+(h[1][x].top())); } h[1][x].push(tmp); } } il int divi(int x,int d,int ff){ //cout<<" x d ff "<<x<<" "<<d<<" "<<ff<<endl; rt=0; dfs1(x,0,d); fa[rt]=ff; dis[rt][d]=0; dfs2(rt,0,d); vis[rt]=1; ans[rt]=-inf; int now=rt; for(reg i=hd[now];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]) continue; nowsz=sz[y]; int son=divi(y,d+1,now); if(h[0][son].size()){ h[1][now].push(h[0][son].top()); } } upda(now); //cout<<" rt "<<now<<" : "<<ans[now]<<endl; hh.push(ans[now]); return now; } il void wrk(int x){ int gg=x; int nd=dep[x]; if(c[x]==0){ c[x]^=1; while(x){ //cout<<" xx nd "<<x<<" "<<nd<<" "<<endl; dd.push(ans[x]); clear(x,1); upda(x); hh.push(ans[x]); if(fa[x]){ if(h[0][x].size()) d[1][fa[x]].push(h[0][x].top()); d[0][x].push(dis[gg][nd-1]); clear(x,0); if(h[0][x].size()) h[1][fa[x]].push(h[0][x].top()); } x=fa[x]; --nd; } } else{ c[x]^=1; while(x){ dd.push(ans[x]); clear(x,1); upda(x); hh.push(ans[x]); if(fa[x]){ if(h[0][x].size()) d[1][fa[x]].push(h[0][x].top()); h[0][x].push(dis[gg][nd-1]); clear(x,0); if(h[0][x].size()) h[1][fa[x]].push(h[0][x].top()); } x=fa[x]; --nd; } } } int main(){ scanf("%d",&n);int x,y,z; for(reg i=1;i<=n-1;++i){ rd(x);rd(y);rd(z); add(x,y,z);add(y,x,z); } nowsz=n; gen=divi(1,1,0); int m; rd(m); char ch[10]; while(m--){ scanf("%s",ch+1); if(ch[1]=='A'){ while(dd.size()&&hh.size()&&hh.top()==dd.top()){ hh.pop();dd.pop(); } if(!hh.size()){ puts("They have disappeared."); } else{ int tmp=hh.top(); if(tmp==-inf) puts("They have disappeared."); else { (tmp<0)&&(tmp=-tmp,putchar('-')); prin(tmp);putchar(' '); } } }else{ rd(x); wrk(x); } // cout<<" ans------- "<<endl; // for(reg i=1;i<=n;++i){ // cout<<i<<" : "<<ans[i]<<endl; // } } return 0; } } int main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/28 9:20:15 */
你也可以顺带AC[ZJOI2007]捉迷藏