zoukankan      html  css  js  c++  java
  • 游戏(并查集)

    题目大意

    • 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
    

    算法分析

    • 显然是并查集(没错我又显然了)
    • 其实暴力算法很好想吧 每次dfs 用vis数组维护是否访问过 如果在一次dfs中访问到了vis标记的点就输出CIKLUS 如果访问到了没有出边的点就输出这个点
    • 但是这样暴力有个很显然的问题 三十万的数据 每次都要初始化一个三十万的数组vis 显然是会T的(我又双显然了)
    • 所以想一想就会想到用并查集维护使得在O(1)时间内完成查询操作 但是显然并查集不能进行删除操作(又双叒显然),因为并查集路径压缩之后是不知道跳到祖先节点的路径的 所以如果进行删除之后当前节点的fa仍然是祖先 相当与没有删除
    • 那该怎么做呢? 先不进行路径压缩,把所有的父亲节点做一次备份,然后把能删除的边都删除,然后后面输入询问的时候把询问存下来 然后倒着跑一遍 如果遇到2的操作就把那个边加上,遇到1就把答案存起来 最后正序输出
    • 关于为什么可以这么做,其实是一个很显然的问题(又双叒叕),因为如果正常顺序的话 ,在当前2操作之前的所有点,都是有x这条边的,而之后的所有点都是没有的,所以就可以了
    • 那如何判环呢? 在最后路径压缩的时候 一开始是一个一个找的父亲 我们用cnt数组记录dfs递归的深度 如果dfs最后超过了n(n个点最多递归n次 如果多了就是有环)就将当前节点的目标位置设置为0(即为环)

    代码展示:

    
    
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 3e5+10,maxm = 2e4+10;
    
    int f[maxn],ff[maxn];
    int a[maxn],flag[maxn];
    int n;
    
    int find(int x,int cnt){if(cnt > n)return f[x] = 0;return x == f[x] ? x : f[x] = find(f[x],++cnt);}//cnt判环
    
    int main(){
    	scanf("%d",&n);
    	for(int i = 1;i <= n;++i)f[i] = i;
    	for(int i = 1;i <= n;++i){int x;scanf("%d",&x);if(x != 0)f[i] = ff[i] = x;}
    	int k;scanf("%d",&k);
    	for(int i = 1;i <= k;++i){scanf("%d%d",&flag[i],&a[i]);if(flag[i] == 2)f[a[i]] = a[i];}//断边
    	for(int i = k;i >= 1;--i){
    		if(flag[i] == 1)a[i] = find(a[i],0);//查询
    		if(flag[i] == 2)f[a[i]] = ff[a[i]];//加边
    	}
    	for(int i = 1;i <= k;++i){
    		if(flag[i] == 1 &&  a[i])printf("%d
    ",a[i]);//输出
    		if(flag[i] == 1 && !a[i])printf("CIKLUS
    ");//没找到
    	}
    	return 0;
    }
    
    
    
  • 相关阅读:
    IE6,IE7 DIV高度技巧(div高度兼容问题)
    QQ在线咨询插件
    Singleton单件 (创建型模式)
    SQL Server ErrorLog 错误日志(如果数据库所占空间变大)
    IE6 div标签height的Bug
    c# 中 Volatile关键字理解
    翻译:SWFObject 2.0官方文档(用来做flash的js)
    已连接到空闲例程的问题解决办法
    Oracle 的一个非常好的触发器例子
    Oracle中表的非常全面的操作
  • 原文地址:https://www.cnblogs.com/2004-08-20/p/13325124.html
Copyright © 2011-2022 走看看