zoukankan      html  css  js  c++  java
  • [ZJOI2007]Hide 捉迷藏

    原文链接: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;
    }
    

      

  • 相关阅读:
    寒假练习集中贴
    7-49 打印学生选课清单 (25分)
    7-47 打印选课学生名单 (25分)
    进阶实验5-3.3 基于词频的文件相似度 (30分)-哈希
    进阶实验5-3.4 迷你搜索引擎 (35分)-哈希
    7-24 树种统计 (25分)-二叉排序树or快速排序
    7-25 朋友圈 (25分)-并查集
    进阶实验6-3.4 拯救007(升级版) (30分)-BFS
    基础实验6-2.3 拯救007 (25分)-DFS
    进阶实验4-3.5 哈夫曼编码 (30分)-最优二叉树
  • 原文地址:https://www.cnblogs.com/cutemush/p/11831933.html
Copyright © 2011-2022 走看看