【并查集】 游戏

游戏
时间限制: 3 Sec  内存限制: 128 MB
提交: 25  解决: 1
[提交][状态][讨论版]
题目描述
	Mirko和Slavko爱玩弹球戏。在一个令人激动的星期五,Mirko和Slavko玩了一把弹球游戏。Mirko构建一个有向图,所有顶点最多有1条出边。弹球从1个顶点出发可以沿着一条边移动到它的邻接点,只要它存在,而且它会继续移动到后者的邻接点去,直到最后到达一个找不到出边的顶点才停下来。如果不存在这样的点,弹球可能无限运动下去。
	为了确信Slavko理解游戏的规则,Mirko将发起一系列询问,询问的类型如下:
	1 X :除非弹球陷入循环,弹球从X出发,最终将在哪个点停下来。
	2 X:删除X的出边(保证该边总是存在)
	注意:询问是按顺序执行的。
输入
	第一行包含一个正整数N(1<=N<=300000),表示图的定点数。
	第二行包含由空格隔开N个正整数,第i个数表示从i顶点可以通过出边到达的定点编号。0表示该点没有出边。
	接下来的一行包含1个整数Q(1<=Q<=300000),表示询问的次数。格式如上所示。
输出
		对于第1类询问,输出弹球停止时所在顶点编号,每行1个,按照查询的顺序输出。如果弹球无法停止,则输出CIKLUS.	
	
样例输入
3
2 3 1
7
1 1
1 2
2 1
1 2
1 1
2 2
1 2
样例输出
CIKLUS
CIKLUS
1
1
2

分析:

                   毫无疑问,最直观的方法肯定是深搜,但是学到这个地步,是个人都知道300000的深搜不超时才有鬼,

于是想到用并查集,虽然输出路的尽头用并查集很容易解决,但是问题是如何删边?

           学过并查集的应该都知道,并查集基本上是不能删边的,只能加边,那么我们想办法把问题转化为加边就可以了。

          先构造出一个所有该删的边都删完了的并查集,再从后往前回答,碰到1直接输出,碰到2就加边,就这么简单


代码如下:

#include
int n,f[300005],m,a[300005][2],b[300005];
int find(int x,int o)
{
    if(o>n) return f[x]=0;//o用来判断死循环
    return x==f[x]?x:f[x]=find(f[x],o+1);
}
int main()
{
    int x;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        if(x!=0) f[i]=b[i]=x;//注意x!=0,不要在这时候find,不然无法删边
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i][0],&a[i][1]);
        if(a[i][0]==2) f[a[i][1]]=a[i][1];//删边
    }
    for(int i=m;i>=1;i--)//从后往前
    {
        if(a[i][0]==1)
        {
            x=find(a[i][1],0);
            a[i][0]=3;
            a[i][1]=x;
        }
        else f[a[i][1]]=b[a[i][1]];//加边
    }
    for(int i=1;i<=m;i++)
        if(a[i][0]==3)
        {
            if(a[i][1]!=0) printf("%d\n",a[i][1]);
            else printf("CIKLUS\n");
        }
}



你可能感兴趣的:(2016普及组模拟考试)