[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治
【bzoj1095】[ZJOI2007]Hide 捉迷藏
Description
捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
Input
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。
Output
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。
Sample Input
8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
Sample Output
4
3
3
4
3
3
4
HINT
对于100%的数据, N ≤100000, M ≤500000。
C.每个重心存所有子树到其距离
B.每个重心存各个子树最大值,即子结点堆C的最大值
A.全局一个堆,维护答案最大值,存每个堆B的最大值和次大值之和
C的意思是u这个节点,经过u,到达其父亲f的路径的所有距离,
B是维护所有的子节点的C的最大值,
A是维护各个B中的最大值。
动态点分治是树上动态添加节点,在这种情况下动态维护点分树的结构
这道题目的树的结构是不变的,所以建出点分树即可,
然后维护各个堆的值即可。
因为点分树上是log层的,堆维护也是log的,所有每次询问logn
修改logn logn,所有总复杂度是n log^2n
计算深度那里十分巧妙,用一个rmq记录,就是通过dfs访问顺序,然后进入一个点记录一次,
这样总共均摊是2*n,和边有关系,
然后rmq记录最小值,发现这个就是lca的深度
然后距离就比较好算了,O(1)算出,
1 #include<cstring> 2 #include<cmath> 3 #include<iostream> 4 #include<cstdio> 5 #include<algorithm> 6 #include<queue> 7 8 #define inf 1000000007 9 #define N 200007 10 #define ll long long 11 using namespace std; 12 inline int read() 13 { 14 int x=0,f=1;char ch=getchar(); 15 while(ch<'0'||ch>'9'){if (ch=='-')f=-1;ch=getchar();} 16 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} 17 return x*f; 18 } 19 20 int n,m,rt,dfn,sum,tot; 21 int bin[22],Log[N]; 22 int siz[N],f[N],dep[N]; 23 int cnt,hed[N],rea[N],nxt[N]; 24 int mn[19][N],pos[N],fa[N]; 25 bool vis[N],clo[N]; 26 struct heap 27 { 28 priority_queue<int> A,B; 29 void push(int x){A.push(x);} 30 void erase(int x){B.push(x);} 31 void pop() 32 { 33 while(B.size()&&A.top()==B.top()) 34 A.pop(),B.pop(); 35 A.pop(); 36 } 37 int top() 38 { 39 while(B.size()&&A.top()==B.top()) 40 A.pop(),B.pop(); 41 if(!A.size())return 0; 42 return A.top(); 43 } 44 int size(){return A.size()-B.size();} 45 int stop() 46 { 47 if(size()<2)return 0; 48 int x=top();pop(); 49 int y=top();push(x); 50 return y; 51 } 52 }A,B[100005],C[100005]; 53 54 void add(int u,int v) 55 { 56 nxt[++cnt]=hed[u]; 57 hed[u]=cnt; 58 rea[cnt]=v; 59 } 60 void dfs(int u,int fa) 61 { 62 mn[0][++dfn]=dep[u],pos[u]=dfn; 63 for (int i=hed[u];i!=-1;i=nxt[i]) 64 { 65 int v=rea[i]; 66 if (v==fa)continue; 67 dep[v]=dep[u]+1; 68 dfs(v,u); 69 mn[0][++dfn]=dep[u]; 70 } 71 } 72 void get_root(int u,int fa) 73 { 74 siz[u]=1,f[u]=0; 75 for (int i=hed[u];i!=-1;i=nxt[i]) 76 { 77 int v=rea[i]; 78 if (v==fa||vis[v])continue; 79 get_root(v,u); 80 siz[u]+=siz[v]; 81 f[u]=max(f[u],siz[v]); 82 } 83 f[u]=max(f[u],sum-siz[u]); 84 if (f[u]<f[rt])rt=u; 85 } 86 void divide(int u,int par) 87 { 88 fa[u]=par,vis[u]=1;int sums=sum; 89 for (int i=hed[u];i!=-1;i=nxt[i]) 90 { 91 int v=rea[i]; 92 if (vis[v])continue; 93 if (siz[v]>siz[u]) sum=sums-siz[u]; 94 else sum=siz[v]; 95 rt=0; 96 get_root(v,u); 97 divide(rt,u); 98 } 99 } 100 int rmq(int x,int y) 101 { 102 x=pos[x],y=pos[y]; 103 if (y<x)swap(x,y); 104 int t=Log[y-x+1]; 105 return min(mn[t][x],mn[t][y-bin[t]+1]); 106 } 107 int dis(int x,int y) 108 { 109 return dep[x]+dep[y]-2*rmq(x,y); 110 } 111 void turn_off(int u,int v) 112 { 113 if (u==v) 114 { 115 B[u].push(0); 116 if (B[u].size()==2)A.push(B[u].top()); 117 } 118 if(!fa[u])return; 119 int f=fa[u],D=dis(f,v),tmp=C[u].top(); 120 C[u].push(D); 121 if (D>tmp) 122 { 123 int mx=B[f].top()+B[f].stop(),size=B[f].size(); 124 if (tmp)B[f].erase(tmp); 125 B[f].push(D); 126 int now=B[f].top()+B[f].stop(); 127 if(now>mx) 128 { 129 if (size>=2)A.erase(mx); 130 if (B[f].size()>=2)A.push(now); 131 } 132 } 133 turn_off(f,v); 134 } 135 void turn_on(int u,int v) 136 { 137 if(u==v) 138 { 139 if(B[u].size()==2)A.erase(B[u].top()); 140 B[u].erase(0); 141 } 142 if(!fa[u])return; 143 int f=fa[u],D=dis(f,v),tmp=C[u].top(); 144 C[u].erase(D); 145 if(D==tmp) 146 { 147 int mx=B[f].top()+B[f].stop(),size=B[f].size(); 148 B[f].erase(D); 149 if(C[u].top())B[f].push(C[u].top()); 150 int now=B[f].top()+B[f].stop(); 151 if(now<mx) 152 { 153 if(size>=2)A.erase(mx); 154 if(B[f].size()>=2)A.push(now); 155 } 156 } 157 turn_on(f,v); 158 } 159 int main() 160 { 161 bin[0]=1;for (int i=1;i<20;i++)bin[i]=bin[i-1]<<1;//2的幂次 162 Log[0]=-1;for (int i=1;i<=200000;i++)Log[i]=Log[i>>1]+1; 163 n=read();memset(hed,-1,sizeof(hed)); 164 for(int i=1;i<n;i++) 165 { 166 int u=read(),v=read(); 167 add(u,v),add(v,u); 168 } 169 170 dfs(1,0);//预处理出深度 171 172 for (int i=1;i<=Log[dfn];i++) 173 for (int j=1;j<=dfn;j++) 174 if (j+bin[i]-1<=dfn) mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]); 175 //预处理,处理出向下走2^x步的到达最浅的点,就是起lca的深度。 176 177 rt=0,f[0]=inf,sum=n; 178 get_root(1,0),divide(rt,0); 179 for (int i=1;i<=n;i++) 180 clo[i]=true; 181 for (int i=1;i<=n;i++)turn_off(i,i); 182 tot=n;//tot记录当前多少灯是灭的。 183 // printf("%d\n",A.top()); 184 185 char ch[2];m=read(); 186 while(m--) 187 { 188 scanf("%s",ch+1); 189 if (ch[1]=='G') 190 { 191 if (tot<=1) printf("%d\n",tot-1); 192 else printf("%d\n",A.top()); 193 } 194 else 195 { 196 int x=read(); 197 if (clo[x])turn_on(x,x),tot--; 198 else turn_off(x,x),tot++; 199 clo[x]^=1; 200 } 201 } 202 }