P2056-[ZJOI2007]捉迷藏【点分树,堆】
生活随笔
收集整理的這篇文章主要介紹了
P2056-[ZJOI2007]捉迷藏【点分树,堆】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目鏈接:https://www.luogu.com.cn/problem/P2056
題目大意
nnn個點的一棵樹,開始全是黑點,有操作
解題思路
根據點分治每個點和分散開來的重心連邊,然后每個點往上只會有logloglog層節點。
對于每個點分樹上節點我們需要維護最長路和次長路,然后要注意它們不能在同一個點分子樹上。
只會對于每個點xxx我們需要維護xxx的點分樹子樹中每個點到xxx的父節點的真實距離的堆。然后還有每個點的所有子節點的堆頂元素的堆。還有一個維護每個節點答案的堆。
時間復雜度O(nlog?n)O(n\log n)O(nlogn)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #include<cctype> #include<queue> #include<set> #define mp(x,y) make_pair(x,y) using namespace std; const int N=1e5+10,inf=1e9,T=18; struct node{int to,next; }a[N*2]; int read(){int x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f; } int n,tot,num,root,q,ls[N],f[N],siz[N],d[N],fa[N]; int dep[N],g[N][T+1];bool v[N],vis[N]; struct heap{priority_queue<int> q1,q2;void insert(int x){q1.push(x);}void erase(int x){q2.push(x);}int top(){while(!q2.empty()&&q1.top()==q2.top())q2.pop(),q1.pop();return q1.top();}int top2(){int w=top();erase(w);int ans=top();insert(w);return ans;}int size(){return q1.size()-q2.size();} }q1[N],q2[N],ans; void addl(int x,int y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return; } void dfs(int x,int fa){dep[x]=dep[fa]+1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;g[y][0]=x;dfs(y,x);}return; } int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=T;i>=0;i--)if(dep[g[y][i]]>=dep[x])y=g[y][i];if(x==y)return x;for(int i=T;i>=0;i--)if(g[x][i]!=g[y][i])x=g[x][i],y=g[y][i];return g[x][0]; } int Dis(int x,int y) {return dep[x]+dep[y]-dep[LCA(x,y)]*2;} void groot(int x,int fa){siz[x]=1;f[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||vis[y])continue;groot(y,x);siz[x]+=siz[y];f[x]=max(f[x],siz[y]);}f[x]=max(f[x],num-siz[x]);if(f[x]<f[root])root=x;return; } void calc(int x,int fa,int sav,int root){int dis=Dis(root,x);q1[sav].insert(dis);for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||vis[y])continue;calc(y,x,sav,root);}return; } int count(int x) {return q2[x].top()+q2[x].top2();} void build(int x){vis[x]=1;q2[x].insert(0);int S=num;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(vis[y])continue;root=0;num=(siz[y]<siz[x])?siz[y]:S-siz[x];groot(y,x);y=root;fa[y]=x;calc(y,x,y,x);q2[x].insert(q1[y].top());build(y);}if(q2[x].size()>1)ans.insert(count(x));return; } int main() {n=read();for(int i=1;i<n;i++){int x=read(),y=read();addl(x,y);addl(y,x);}//讀入dfs(1,1);for(int j=1;j<=T;j++)for(int i=1;i<=n;i++)g[i][j]=g[g[i][j-1]][j-1];//預處理求LCA f[0]=inf;num=n;groot(1,1);build(root);num=n;memset(v,0,sizeof(v));scanf("%d",&q);while(q--){char op[3];int x;scanf("%s",op);if(op[0]=='C'){x=read();if(!v[x]){v[x]=1;num--;if(q2[x].size()>1)ans.erase(count(x));q2[x].erase(0);if(q2[x].size()>1)ans.insert(count(x));for(int y=x;fa[y];y=fa[y]){if(q2[fa[y]].size()>1)ans.erase(count(fa[y]));q2[fa[y]].erase(q1[y].top());q1[y].erase(Dis(x,fa[y]));if(q1[y].size())q2[fa[y]].insert(q1[y].top());if(q2[fa[y]].size()>1)ans.insert(count(fa[y]));}}else{v[x]=0;num++;if(q2[x].size()>1)ans.erase(count(x));q2[x].insert(0);if(q2[x].size()>1)ans.insert(count(x));for(int y=x;fa[y];y=fa[y]){if(q2[fa[y]].size()>1)ans.erase(count(fa[y]));if(q1[y].size())q2[fa[y]].erase(q1[y].top());q1[y].insert(Dis(x,fa[y]));q2[fa[y]].insert(q1[y].top());if(q2[fa[y]].size()>1)ans.insert(count(fa[y]));}}}if(op[0]=='G'){if(num<=1)printf("%d\n",num-1);else printf("%d\n",ans.top());}}return 0; }總結
以上是生活随笔為你收集整理的P2056-[ZJOI2007]捉迷藏【点分树,堆】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 勒索病毒层出不穷勒索病毒真的无解么
- 下一篇: jzoj5699-[GDOI2018da