zoukankan      html  css  js  c++  java
  • BZOJ 2049洞穴探测

    辉辉热衷于洞穴勘测。某天,他按照地图来到了一片被标记为JSZX的洞穴群地区。经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴。假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径。洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通道,有时这条通道又会因为某种稀奇古怪的原因被毁。辉辉有一台监测仪器可以实时将通道的每一次改变状况在辉辉手边的终端机上显示:如果监测到洞穴u和洞穴v之间出现了一条通道,终端机上会显示一条指令 Connect u v 如果监测到洞穴u和洞穴v之间的通道被毁,终端机上会显示一条指令 Destroy u v 经过长期的艰苦卓绝的手工推算,辉辉发现一个奇怪的现象:无论通道怎么改变,任意时刻任意两个洞穴之间至多只有一条路径。因而,辉辉坚信这是由于某种本质规律的支配导致的。因而,辉辉更加夜以继日地坚守在终端机之前,试图通过通道的改变情况来研究这条本质规律。然而,终于有一天,辉辉在堆积成山的演算纸中崩溃了……他把终端机往地面一砸(终端机也足够坚固无法破坏),转而求助于你,说道:“你老兄把这程序写写吧”。辉辉希望能随时通过终端机发出指令 Query u v,向监测仪询问此时洞穴u和洞穴v是否连通。现在你要为他编写程序回答每一次询问。已知在第一条指令显示之前,JSZX洞穴群中没有任何通道存在。

    Input

    第一行为两个正整数n和m,分别表示洞穴的个数和终端机上出现过的指令的个数。以下m行,依次表示终端机上出现的各条指令。每行开头是一个表示指令种类的字符串s("Connect”、”Destroy”或者”Query”,区分大小写),之后有两个整数u和v (1≤u, v≤n且u≠v) 分别表示两个洞穴的编号。

    Output

    对每个Query指令,输出洞穴u和洞穴v是否互相连通:是输出”Yes”,否则输出”No”。(不含双引号)

    Sample Input样例输入1 cave.in 200 5 Query 123 127 Connect 123 127 Query 123 127 Destroy 127 123 Query 123 127 样例输入2 cave.in 3 5 Connect 1 2 Connect 3 1 Query 2 3 Destroy 1 3 Query 2 3

    Sample Output样例输出1 cave.out No Yes No 样例输出2 cave.out Yes No Hint

    数据说明 10%的数据满足n≤1000, m≤20000 20%的数据满足n≤2000, m≤40000 30%的数据满足n≤3000, m≤60000 40%的数据满足n≤4000, m≤80000 50%的数据满足n≤5000, m≤100000 60%的数据满足n≤6000, m≤120000 70%的数据满足n≤7000, m≤140000 80%的数据满足n≤8000, m≤160000 90%的数据满足n≤9000, m≤180000 100%的数据满足n≤10000, m≤200000 保证所有Destroy指令将摧毁的是一条存在的通道本题输入、输出规模比较大,建议cc++选手使用scanf和printf进行IO操作以免超时

    题解:直接上动态树即可,模板题

    参考代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define RI register int
     4 inline int read()
     5 {
     6     int f=1,x=0;char ch=getchar();
     7     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
     8     while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
     9     return f*x;
    10 }
    11 const int N=200009;
    12 int f[N],c[N][2],s[N],st[N];
    13 bool r[N];
    14 //判断节点是否为一个Splay的根(与普通Splay的区别1) 
    15 //原理很简单,如果连的是轻边,他的父亲的儿子里没有它
    16 inline bool nroot(RI x){ return c[f[x]][0]==x || c[f[x]][1]==x; }
    17 inline void pushr(RI x){ RI t=c[x][0];c[x][0]=c[x][1];c[x][1]=t;r[x]^=1; }//翻转操作
    18 inline void pushdown(RI x)//判断并释放懒标记
    19 {
    20     if(r[x])
    21     {
    22         if(c[x][0]) pushr(c[x][0]);
    23         if(c[x][1]) pushr(c[x][1]);
    24         r[x]=0;
    25     }
    26 }
    27 inline void rotate(RI x)//一次旋转
    28 {
    29     RI y=f[x],z=f[y],k = c[y][1]==x,w=c[x][!k];
    30     if(nroot(y)) c[z][c[z][1]==y]=x; c[x][!k]=y;c[y][k]=w;
    31     //额外注意if(nroot(y))语句此处不判断会引起致命错误(与普通Splay的区别2)
    32     if(w) f[w]=y; f[y]=x;f[x]=z;
    33 }
    34 inline void splay(RI x)//只传了一个参数因为所有操作的目标都是该Splay的根(与普通Splay的区别3)
    35 {
    36     RI y=x,z=0;
    37     st[++z]=y;//st为栈,暂存当前点到根的整条路径,pushdown时一定要从上往下放标记(与普通Splay的区别4)
    38     while(nroot(y)) st[++z]=y=f[y];
    39     while(z) pushdown(st[z--]);
    40     while(nroot(x))
    41     {
    42         y=f[x];z=f[y];
    43         if(nroot(y)) rotate( (c[y][0]==x)^(c[z][0]==y) ? x:y );
    44         rotate(x);
    45     }
    46 }
    47 inline void access(RI x){//访问
    48     for(RI y=0;x;x=f[y=x])
    49         splay(x),c[x][1]=y;
    50 }
    51 inline void makeroot(RI x){//换根
    52     access(x);splay(x);
    53     pushr(x);
    54 }
    55 inline int findroot(RI x){//找根(在真实的树中的)
    56     access(x);splay(x);
    57     while(c[x][0]) pushdown(x),x=c[x][0];
    58     return x;
    59 }
    60 inline void split(RI x,RI y){//提取路径
    61     makeroot(x);
    62     access(y);splay(y);
    63 }
    64 inline void link(RI x,RI y){//连边
    65     makeroot(x);
    66     if(findroot(y)!=x)f[x]=y;
    67 }
    68 inline void cut(RI x,RI y)//断边
    69 {
    70     makeroot(x);
    71     if(findroot(y)==x&&f[x]==y&&!c[x][1])
    72     {
    73         f[x]=c[y][0]=0;
    74     }
    75 }
    76 inline void query(RI x,RI y)
    77 {
    78     int fx=findroot(x);
    79     int fy=findroot(y);
    80     if(fx==fy) puts("Yes");
    81     else puts("No");
    82 }
    83 int n,m,u,v;
    84 char ch[20];
    85 int main()
    86 {
    87     scanf("%d%d",&n,&m);
    88     while(m--)
    89     {
    90         scanf("%s",ch);
    91         scanf("%d%d",&u,&v);
    92         if(ch[0]=='C') link(u,v);
    93         else if(ch[0]=='D') cut(u,v);
    94         else if(ch[0]=='Q') query(u,v);
    95     }
    96     return 0;
    97 }
    View Code
  • 相关阅读:
    JavaEE中Filter实现用户登录拦截
    【Tomcat】如何注册Tomcat到Window Service服务
    案例分析:项目组内踢皮球事件
    最大子序列求和问题
    《游戏脚本的设计与开发》-第一部分总结 文字脚本的功能扩展和一个游戏测试
    ajax异步请求实例
    创建用于编译和运行Java程序的批处理文件
    Codeforces Round #189 (Div. 2)
    新的研究方向
    怎样在android实现uc和墨迹天气那样的左右拖动效果
  • 原文地址:https://www.cnblogs.com/csushl/p/10314259.html
Copyright © 2011-2022 走看看