原文链接:https://blog.csdn.net/qq_41552508/article/details/101171460
捉迷藏 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行,每行一个操作,如
上文所示。
N ≤100000, M ≤500000。
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
Sample Output
4
3
3
4
这道题的做法有括号序列、动态点分治、线段树维护直径。此处只介绍线段树维护直径的做法。
首先我们求一个 dfs序,然后在dfs序上建线段树,对于线段树的每个区间,我们维护两个点表示在这个区间中相距最远的两个0点。区间合并时我们只需要取出这两个区间所维护的点,然后对这四个点两两求距离更新答案即可。
这样做的原因在于对于两个子树来说,每个子树都有一条属于该子树的直径,则两个子树合并后的新直径必定是从原来两个子树中的4个点中选取2个点作为答案。
大致思路就是这样,具体细节见代码。
#include <bits/stdc++.h> #define rep(i,a,b) for(int i = a; i <= b; i++) typedef long long ll; const int N = 1e5+100; using namespace std; int n,m,dis[N],tot,head[N],f[N][25],dfn[N],rk[N],flag[N],T; pair<int,int> sgt[4*N]; struct Edge{ int to,next; }e[2*N]; inline void add(int x,int y){ e[++tot].to = y, e[tot].next = head[x], head[x] = tot; } void dfs(int x,int fa){ dis[x] = dis[fa]+1; f[x][0] = fa; dfn[x] = ++tot; rk[tot] = x; for(int i = 1; (1<<i) <= dis[x]; i++) f[x][i] = f[f[x][i-1]][i-1]; for(int i = head[x]; i; i = e[i].next){ int y = e[i].to; if(y == fa) continue; dis[y] = dis[x]+1; dfs(y,x); } } inline int lca(int x,int y){ if(dis[x] > dis[y]) swap(x,y); for(int i = T; i >= 0; i--) if(dis[f[y][i]] >= dis[x]) y = f[y][i]; if(x == y) return x; for(int i = T; i >= 0; i--) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } inline int dist(int x,int y){ if(x == 0 && y == 0) return -1; if(x == 0 || y == 0) return 0; return dis[x]+dis[y]-2*dis[lca(x,y)]; } inline pair<int,int> pushUp(pair<int,int> x,pair<int,int> y){ int a[5],res = 0,cnt = 0,tmp; pair<int,int> base = make_pair(0,0); a[0] = 0; if(x.first && flag[x.first]) a[++cnt] = x.first; if(x.second && flag[x.second]) a[++cnt] = x.second; if(y.first && flag[y.first]) a[++cnt] = y.first; if(y.second && flag[y.second]) a[++cnt] = y.second; rep(i,1,cnt-1) rep(j,i+1,cnt) if((tmp=dist(a[i],a[j])) > res) res = tmp, base = make_pair(a[i],a[j]); if(res == 0) base = make_pair(a[cnt],a[cnt]); return base; } inline void build(int now,int l,int r){ if(l == r) sgt[now] = make_pair(rk[l],rk[l]), flag[rk[l]] = 1; else{ int mid = (l+r)>>1; build(now<<1,l,mid); build(now<<1|1,mid+1,r); sgt[now] = pushUp(sgt[now<<1],sgt[now<<1|1]); } } inline void update(int now,int l,int r,int pos){ if(l == r){ flag[rk[l]] ^= 1; if(flag[rk[l]]) sgt[now] = make_pair(rk[l],rk[l]); else sgt[now] = make_pair(0,0); return; } int mid = (l+r)>>1; if(pos <= mid) update(now<<1,l,mid,pos); else update(now<<1|1,mid+1,r,pos); sgt[now] = pushUp(sgt[now<<1],sgt[now<<1|1]); } int main() { scanf("%d",&n); tot = 1; T = (int)(log(n)/log(2))+1; rep(i,1,n-1){ int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } tot = 0; dfs(1,0); build(1,1,n); scanf("%d",&m); while(m--){ char op[10]; scanf("%s",op); if(op[0] == 'C'){ int x; scanf("%d",&x); update(1,1,n,dfn[x]); } else printf("%d ",dist(sgt[1].first,sgt[1].second)); } return 0; }