BZOJ1095 [ZJOI2007]捉迷藏 动态点分治

来源:互联网 发布:淘宝套现可靠的店家 编辑:程序博客网 时间:2024/06/05 19:58

每次修改一个点的黑白状态,询问树上最远黑点距离

拿这个题做动态点分治模板题:(%%%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;}


3 0
原创粉丝点击