每次修改一个点的黑白状态,询问树上最远黑点距离
拿这个题做动态点分治模板题:(%%%PoPoQQQ大爷)
点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中“最高重心”上,从所有的重心处像分治中的不同子树索取到重心的链,就可以覆盖所有链的信息。动态点分治就像把序列分治变成线段树一样,在分治的架子上加了信息维护,实现树链信息维护与查询。
需要什么?
每个重心需要其每个分离子树到它的信息(很重要,否则形成链的重复部分,并且还需要一个自己到自己的空信息维护单链上来的信息)
每个重心需要它到父分治块的信息
全局需要维护每个重心的信息
因此,考虑问题的静态版本,需要维护每个节点每个子树内的最长链,那么为了修改,最大化应换成堆维护。
每个重心维护一个堆,表示其分离子树中每个到它的最长链
每个重心维护一个堆,表示其块内到父重心的最长链
全局维护一个堆,表示每个重心处的最长链
修改时只要从一个节点作为重心的块开始向上修改,就可以遍历到所有包含它的块,修改到父重心的信息,修改父重心的信息
注意开始时的节点的分离子树那个堆要插一个0表示单链,而改掉节点值的时候0的存在性也要相应地变化。
堆也比较有技巧:封装双堆分别为值和删了的值,取的时候两堆顶相同则pop,相当于变种删除标记
LCA倍增写错:要返回p[x][0](QAQ)
#include<queue> #include<cstdio> #include<iostream> using namespace std; const int Mn=100005; int cnt=0,h[Mn],n,m,vst[Mn],maxx,tg,s[Mn],sz,prt[Mn]; int d[Mn],p[Mn][21],co[Mn]; struct Priority_Queue{ priority_queue<int>q,del; void push(int x){q.push(x);} void erase(int x){del.push(x);} int top(){ while(del.size()&&del.top()==q.top()) {del.pop();q.pop();} return q.top(); } void Pop(){ while(del.size()&&del.top()==q.top()) {del.pop();q.pop();} q.pop(); } int sec_top(){ int tmp=top();Pop(); int se=top();push(tmp); return se; } int size(){return q.size()-del.size();} }c[Mn],f[Mn],ans; struct Edge{int to,next;}w[Mn*2]; void AddEdge(int x,int y){w[++cnt]=(Edge){y,h[x]};h[x]=cnt;} void DFS(int x,int fa){ int j,y; p[x][0]=fa; for(j=1;j<=20;j++)p[x][j]=p[p[x][j-1]][j-1]; for(j=h[x];j;j=w[j].next){ y=w[j].to; if(y==fa)continue; d[y]=d[x]+1; DFS(y,x); } } void Insert(Priority_Queue &s){ if(s.size()>1)ans.push(s.top()+s.sec_top()); } void Erase(Priority_Queue &s){ if(s.size()>1)ans.erase(s.top()+s.sec_top()); } void init(){ int i,x,y; scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); AddEdge(x,y);AddEdge(y,x); } d[1]=1;DFS(1,0); } void DP(int x,int fa){ int j,y;s[x]=1; for(j=h[x];j;j=w[j].next){ y=w[j].to; if(y==fa||vst[y])continue; DP(y,x); s[x]+=s[y]; } } void Biggest(int x,int fa){ int j,y,mx=0; for(j=h[x];j;j=w[j].next){ y=w[j].to; if(y==fa||vst[y])continue; Biggest(y,x); mx=max(mx,s[y]); } if(maxx>max(mx,sz-s[x])){ maxx=max(mx,sz-s[x]); tg=x; } } int FindCen(int x){ maxx=n+1;tg=0; DP(x,0); sz=s[x]; Biggest(x,0); return tg; } int LCA(int x,int y){ int j; if(d[x]<d[y])swap(x,y); for(j=20;j>=0;j--) if(d[p[x][j]]>=d[y])x=p[x][j]; if(x==y)return x; for(j=20;j>=0;j--) if(p[x][j]!=p[y][j]) {x=p[x][j];y=p[y][j];} return p[x][0]; } int Dis(int x,int y){return d[x]+d[y]-2*d[LCA(x,y)];} void work(int x,int fa,int Gra){ int j,y; f[Gra].push(Dis(x,prt[Gra])); for(j=h[x];j;j=w[j].next){ y=w[j].to; if(y==fa||vst[y])continue; work(y,x,Gra); } } int DivOnT(int x,int fa){ int j,y,G,Gy; G=FindCen(x);prt[G]=fa; work(G,0,G); vst[G]=1; c[G].push(0); for(j=h[G];j;j=w[j].next){ y=w[j].to; if(!vst[y]){ Gy=DivOnT(y,G); c[G].push(f[Gy].top()); } } Insert(c[G]); return G; } void Light(int x){ Erase(c[x]); c[x].erase(0); Insert(c[x]); for(int y=x;prt[y];y=prt[y]){ Erase(c[prt[y]]); if(f[y].size())c[prt[y]].erase(f[y].top()); f[y].erase(Dis(x,prt[y])); if(f[y].size())c[prt[y]].push(f[y].top()); Insert(c[prt[y]]); } } void LiOut(int x){ Erase(c[x]); c[x].push(0); Insert(c[x]); for(int y=x;prt[y];y=prt[y]){ Erase(c[prt[y]]); if(f[y].size())c[prt[y]].erase(f[y].top()); f[y].push(Dis(x,prt[y])); if(f[y].size())c[prt[y]].push(f[y].top()); Insert(c[prt[y]]); } } void solve(){ int i,x;char ch[5]; cnt=n; scanf("%d",&m); for(i=1;i<=m;i++){ scanf("%s",ch); if(ch[0]=='G'){ if(cnt<=1)printf("%d\n",cnt-1); else printf("%d\n",ans.top()); } else{ scanf("%d",&x); if(!co[x]){cnt--;Light(x);co[x]=1;} else{cnt++;LiOut(x);co[x]=0;} } } } int main(){ init(); DivOnT(1,0); solve(); return 0; }