zoukankan      html  css  js  c++  java
  • 【洛谷P2056】捉迷藏

    题目

    题目链接:https://www.luogu.com.cn/problem/P2056
    Jiajia 和 Wind 是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind 和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由 (N) 个屋子和 (N-1) 条双向走廊组成,这 (N-1) 条走廊的分布使得任意两个屋子都互相可达。
    游戏是这样进行的,孩子们负责躲藏,Jiajia 负责找,而 Wind 负责操纵这 (N) 个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia 希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。
    我们将以如下形式定义每一种操作:

    • C(hange) i 改变第 (i) 个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
    • G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。

    (nleq 10^5,mleq 5 imes 10^5)

    思路

    建出点分树,对于点分树上每一个点,维护两个堆 (q1,q2)。其中 (q1) 放点分树上在这个点 (x) 为根的子树中,所有点到点分树上 (x) 父亲的距离。(q2) 放点 (x) 所有子树的堆的最大值。
    那么显然答案就是每一个点的 (q2) 的最大值和次大值之和的最大值。所以我们再开一个堆存所有点答案的最大值。
    对于一次修改操作,我们从 (x) 往上跳父亲,更新它与父亲的堆即可。
    对于查询操作,就直接输出答案堆的堆顶。注意只有一个关灯房间和没有关灯房间需要特判。
    但是普通的堆是不支持删除的,所以我们再开三个堆分别对应前文三个堆,如果要在某一个堆中删除,那么就在其对应的堆中插入,需要时不断判断两个堆顶是不是相同即可。
    时间复杂度 (O(mlog^2 n))

    代码

    常数不是很优秀,开了 O2 才过。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,LG=18,Inf=1e9;
    int n,m,rt,tot,cnt,head[N],fa[N],dep[N],maxp[N],size[N],f[N][LG+1];
    bool vis[N];
    char ch[3];
    priority_queue<int> q1[N],q2[N],d1[N],d2[N],qans,dans;
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void dfs1(int x,int _fa)
    {
    	f[x][0]=_fa; dep[x]=dep[_fa]+1;
    	for (int i=1;i<=LG;i++)
    		f[x][i]=f[f[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=_fa) dfs1(v,x);
    	}
    }
    
    void findrt(int x,int _fa,int sum)
    {
    	size[x]=1; maxp[x]=0;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!vis[v] && v!=_fa)
    		{
    			findrt(v,x,sum);
    			size[x]+=size[v];
    			if (size[v]>maxp[x]) maxp[x]=size[v];
    		}
    	}
    	if (sum-size[x]>maxp[x]) maxp[x]=sum-size[x];
    	if (maxp[x]<maxp[rt]) rt=x;
    }
    
    void dfs2(int x,int _fa,int sum)
    {
    	vis[x]=1; fa[x]=_fa;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (!vis[v])
    		{
    			int s=(size[v]<size[x])?size[v]:sum-size[x];
    			rt=0;
    			findrt(v,0,s); dfs2(rt,x,s);
    		}
    	}
    }
    
    int lca(int x,int y)
    {
    	if (dep[x]<dep[y]) swap(x,y);
    	for (int i=LG;i>=0;i--)
    		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if (x==y) return x;
    	for (int i=LG;i>=0;i--)
    		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    	return f[x][0];
    } 
    
    void updans(int x,bool flag)
    {
    	if (!q2[x].size()) return;
    	int tmp=q2[x].top(); q2[x].pop();
    	while (q2[x].size() && d2[x].size() && q2[x].top()==d2[x].top())
    		q2[x].pop(),d2[x].pop();
    	if (!flag && q2[x].size()) qans.push(q2[x].top()+tmp);
    	if (flag && q2[x].size()) dans.push(q2[x].top()+tmp);
    	q2[x].push(tmp);
    }
    
    void update(int x,bool flag)
    {
    	updans(x,1);
    	if (flag) d2[x].push(0);
    		else q2[x].push(0);
    	while (q2[x].size() && d2[x].size() && q2[x].top()==d2[x].top())
    		q2[x].pop(),d2[x].pop();
    	updans(x,0);
    	for (int i=x;fa[i];i=fa[i])
    	{
    		int dis=dep[x]+dep[fa[i]]-2*dep[lca(x,fa[i])];
    		updans(fa[i],1);
    		if (q1[i].size()) d2[fa[i]].push(q1[i].top());
    		if (flag) d1[i].push(dis);
    			else q1[i].push(dis);
    		while (q1[i].size() && d1[i].size() && q1[i].top()==d1[i].top())
    			q1[i].pop(),d1[i].pop();
    		if (q1[i].size()) q2[fa[i]].push(q1[i].top());
    		while (q2[fa[i]].size() && d2[fa[i]].size() && q2[fa[i]].top()==d2[fa[i]].top())
    			q2[fa[i]].pop(),d2[fa[i]].pop();
    		updans(fa[i],0);
    	}
    	while (qans.size() && dans.size() && qans.top()==dans.top())
    		qans.pop(),dans.pop();
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d",&n);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	rt=0; maxp[0]=Inf;
    	dfs1(1,0);
    	findrt(1,0,n); dfs2(rt,0,n);
    	memset(vis,0,sizeof(vis));
    	for (int i=1;i<=n;i++)
    		update(i,0);
    	cnt=n;
    	scanf("%d",&m);
    	while (m--)
    	{
    		scanf("%s",ch);
    		if (ch[0]=='G')
    		{
    			if (!cnt) printf("-1
    ");
    			else if (cnt==1) printf("0
    ");
    			else printf("%d
    ",qans.top());
    		}
    		else
    		{
    			int x;
    			scanf("%d",&x);
    			vis[x]^=1;
    			update(x,vis[x]);
    			if (vis[x]==1) cnt--;
    				else cnt++;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    MySql常用命令
    多线程
    redhat7.7(centOS7)安装ORACLE 11g出坑教程及问题总结与解决
    使用 CGImageRef 出现的crash
    leetcode238. 除自身以外数组的乘积
    通过位运算实现求和
    leetcode求1+2+……+n
    leetcode101. 对称二叉树
    leetcode198. 打家劫舍
    leetcode394. 字符串解码
  • 原文地址:https://www.cnblogs.com/stoorz/p/14228524.html
Copyright © 2011-2022 走看看