COCI2010/2011 Contest#7 E

COCI2010/2011 Contest#7 E

题意:

​ 小C 和小T 在玩图论游戏,在一个不平凡的周五,小C 想到了一个有趣的图论游戏,想和小T 玩。

​ 在游戏中,小C 展示出一个有向图,图中有 \(n\) 个节点,图中每一个顶点最多抵达一个顶点,如果选中了顶点X,那么会一直沿着点运动的方向运动下去直到某个顶点出度为0(即抵达不了任何点);

​ 为了确保小T已经理解的规则,小C问了一系列的问题,问题分为两类:

  • 1 \(x\) 除非最终陷入某个在环中,否则回答从X顶点出发抵达的最远的点
  • 2 \(x\) 删除以X为起点的边(保证一定有这样的边);

​ 比较套路的题目。对于这种删边的题目,我们可以运用正难则反的思维方式,我们将其离线,倒着做,从最后一个查询往前做起,这样删边就变成了加边。原本是将一个集合分成两个集合。现在则是将两个集合合并为一个集合。对于前者我们没有什么特别好的算法去维护,但是后者我们可以轻而易举的用并查集来维护。我们先将询问中要求我们删的边删去。然后遍历一遍 \(n\) 个节点。将 \(f_i\) 设置为 \(to_i\)。(其中 \(f\) 为并查集数组,\(to_i\)\(i\) 指向的点)如果 \(f_i=i\) 那么说明有一个环,开一个 \(cir\) 数组,将 \(cir_i\) 设置为 \(\text{true}\)。然后倒着处理询问,加边的操作同上。然后我们将询问的答案记下来按顺序输出就好。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5+5;
int n,q,f[MAXN],far[MAXN],to[MAXN],tot,ans[MAXN];
bool cir[MAXN],vis[MAXN],cut[MAXN];
struct Ques
{
	int op,x;
}Q[MAXN];
int find(int x)
{
	return x==f[x]?x:f[x]=find(f[x]);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		scanf("%d",&to[i]);
	for(int i=1;i<=n;++i) f[i]=i;
	scanf("%d",&q);
	for(int i=1;i<=q;++i)
		scanf("%d %d",&Q[i].op,&Q[i].x),cut[Q[i].x]=cut[Q[i].x]|(Q[i].op==2);
	for(int i=1;i<=n;++i)
	{
		if(cut[i]||!to[i]) continue;
		if(find(to[i])==i) cir[i]=1;
		else f[i]=to[i];
	}
	for(int i=q;i>=1;--i)
	{
		if(Q[i].op==1)
		{
			if(cir[Q[i].x]||cir[find(Q[i].x)]) ans[i]=-1;
			else ans[i]=find(Q[i].x);
		}
		else
		{
			if(Q[i].x==find(to[Q[i].x]))
				cir[Q[i].x]=1;
			else f[Q[i].x]=to[Q[i].x];
		}
	}
	for(int i=1;i<=q;++i)
	{
		if(!ans[i]) continue;
		if(ans[i]==-1) printf("CIKLUS\n");
		else printf("%d\n",ans[i]);
	}
	return 0;
}

总结:

​ 比较常规的套路题,如果遇到这种删除操作为主的题目,考虑离线。主要体现一个正难则反的思想,模拟赛中没写出来,还是因为写的题太少了。考场中我还想到一种在线的方法,我们动态建树,对每条链建一棵线段树。在操作中如果环被断成了链,也对它再建一棵树,那么链中的元素被删除就是将一个区间修改成某个特定值的操作。后来去查了一下,确实是有 LCT 的做法。(因为不会写所以考场上没敲出来)黑科技还是蛮重要的。

posted @ 2021-06-28 14:28  夜空之星  阅读(48)  评论(0编辑  收藏  举报