noip 2017 模拟 D1 T3

题目背景

在一个叫做Travian的世界里,生活着各个大大小小的部落。其中最为强大的是罗马、高卢和日耳曼。他们之间为了争夺资源和土地,进行了无数次的战斗。期间诞生了众多家喻户晓的英雄人物,也留下了许多可歌可泣的动人故事。

其中,在大大小小的部落之间,会有一些道路相连,这些道路是Travian世界里的重要枢纽,简单起见,你可以把这些部落与部落之间相连的道路看作一颗树,可见每条道路对于Travian世界的重要程度。有了这些道路,建筑工人就可以通过这些道路进行友好外交啦。

然而,事情并不会像想象的那样美好,由于资源的匮乏,相邻的部落(由一条道路相连的部落)之间经常会发生大大小小的冲突事件,更有甚者,会升级为部落之间的大型战争。

为了避免误伤,每当两个相邻的部落之间发生大型战争之时,这两个部落间的道路是不允许通行的,对于一些强大的部落,甚至能与多个相邻的部落同时开战,同样的,这些战争地带的道路十分危险,是不可通行的。

天下之势,分久必合,当两个部落经历了不打不相识的苦战之后,他们可以签订停战协议(暂时停战,以后依旧可能再次开战),这样,两个部落之间的道路又会重新恢复为可通行状态,建筑工人们又可以经过此地购买最新的大本营设计图纸来强大自己的部落了。

为了简单起见,我们把各大战争事件按发起的时间顺序依次编号(最先发起的战争编号就为 1,第二次战争编号就为 2,以此类推),当两个部落停战之时,则会直接告诉你这场战争的编号,然后这场战争就载入了史册,不复存在了,当然,这并不会影响到其他战争的编号。

建筑工人十分讨厌战争,因为战争,想从一个部落到另一个部落进行友好交流的建筑工人可能就此白跑一趟。所以,在他们出发之前,都会向你问问能不能到达他们想去的部落。

题目描述

简单起见,你就是要处理下面三件事,所有的事件都是按照时间顺序给出的。

1.QQ pp qq从第 pp 个部落出发的建筑工人想知道能否到达第 qq 个部落了,你要回答的便是(Yes/No),注意大小写

2.CC pp qq第 pp 个部落与第 qq 个部落开战了,保证他们一定是相邻的部落,且目前处于停战(未开战)状态

3.UU xx ) 第 xx 次发生的战争结束了,它将永远的被载入史册,不复存在(保证这个消息不会告诉你多次)

输入输出格式

输入格式:

第一行两个数 nn 和 mm, nn 代表了一共有 nn 个部落,mm 代表了以上三种事件发生的总数

接下来的 n - 1n1 行,每行两个数 pp , qq,代表了第 pp 个部落与第 qq 个部落之间有一条道路相连

接下来的 mm 行,每行表示一件事,详见题目描述

输出格式:

每行一个“YesYes”或者“NoNo”,表示从第 pp 个部落出发的建筑工人能否到达第 qq 个部落

输入输出样例

输入样例#1:  复制
5 9
1 2
2 3
3 4
4 5
Q 1 4
C 2 1
C 4 3
Q 3 1
Q 1 5
U 1
U 2
C 4 3
Q 3 4
输出样例#1:  复制
Yes
No
No
No
输入样例#2:  复制
10 10
1 2
1 3
3 4
3 5
1 6
3 7
1 8
2 9
5 10
C 8 1
Q 6 1
C 2 1
Q 2 10
U 1
C 9 2
C 7 3
U 3
Q 6 7
Q 1 10
输出样例#2:  复制
Yes
No
No
Yes
输入样例#3:  复制
20 20
1 2
1 3
2 4
1 5
1 6
4 7
1 8
2 9
5 10
1 11
2 12
7 13
1 14
1 15
11 16
4 17
3 18
18 19
8 20
Q 13 5
C 14 1
C 16 11
U 1
U 2
C 20 8
Q 7 1
C 7 4
Q 17 17
Q 1 6
C 16 11
C 2 1
Q 16 2
U 3
U 5
U 6
C 2 1
C 6 1
C 13 7
C 11 1
输出样例#3:  复制
Yes
Yes
Yes
Yes
No

说明

对于30%的数据 1<=n,m<=6000

对于另30%的数据,保证部落之间的地理关系是一条链,且 i 与 i + 1 之间有一条道路

对于另30%的数据,1<=n,m<=100000

对于100%的数据,1<=n,m<=300000









































/***************************************************
 * 查询两点是否连通,相当于查询两点之间是否有断点
 * 那么考虑树上差分,维护每个点到根节点的路径上,断点
 * 的个数,直接按照DFS序,用树状数组维护即可
 **************************************************/
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 300300;
const int maxm = 300300;
int n, m, tot, coc, root;
vector <int> G[ maxn ];
struct BIT{
    int s[ maxn << 1 ];
    void add(int p,int v){
        for(int i = p ; i <= n << 1 ; i += i & (-i) )
            s[ i ] += v;
    }
    int query(int p){
        int rtn = 0;
        for(int i = p ; i ; i -= i & (-i) )
            rtn += s[ i ];
        return rtn;
    }
}dis;

int dep[ maxn ], son[ maxn ], fa[ maxn ], sz[ maxn ], top[ maxn ], pin[ maxn ], pout[ maxn ];
int Q[ maxm ];
void dfs1(int k){
    sz[ k ] = 1;
    for(int i = 0; i < G[ k ].size(); ++i){
        int kk = G[ k ][ i ];
        if( kk == fa[ k ] ) continue;
        fa[ kk ] = k;
        dep[ kk ] = dep[ k ] + 1;
        dfs1( kk );
        if( sz[ son[ k ] ] < sz[ kk ] ) son[ k ] = kk;
        sz[ k ] += sz[ kk ];
    }
}
void dfs2(int k, int t){
    top[ k ] = t;
    pin[ k ] = ++coc;
    if( !son[ k ] ) return ( void ) (pout[ k ] = ++coc);
    dfs2( son[k] , t );
    for(int i = 0; i < G[ k ].size(); ++i){
        int kk = G[ k ][ i ];
        if( kk == fa[ k ] || kk == son[ k ] )continue;
        dfs2( kk , kk );
    }
    return ( void ) (pout[ k ] = ++coc);
}
int lca(int x, int y){
    while( top[ x ] != top[ y ] ){
        if( dep[ top[ x ] ] < dep[ top[ y ] ] ) swap(x, y);
        x = fa[ top[ x ] ];
    }return dep[ x ] < dep[ y ] ? x : y;
}
struct io {
    char op[ 1 << 25 ] , *s;
    io(){
        //freopen("lct.in", "r", stdin);
        //freopen("lct.out", "w", stdout);
        fread( s = op , 1 , 1 << 25 , stdin );
    }  
    int read(){
        int u = 0;
        while( *s < 48 || *s > 57 )s++;
        while( *s > 47 && *s < 58 )u = ( u << 1 ) + ( u << 3 ) + *s++ - 48;
        return u;
    }
    char gc(){
        while( *s < 'A' || *s > 'Z' )s++;
        return *s++ ;
    }
}ip;
#define read ip.read
#define gcha ip.gc
int main(){
    
    n = read(); m = read();
    for(int i = 1; i < n; ++i){
        int x = read() , y = read();
        G[ x ].push_back( y );
        G[ y ].push_back( x );
    }
    root = n / 2;
    dfs1( root );
    dfs2( root , root );
    for(int i = 1; i <= n; ++i){
        dis.add( pin[ i ], 1 );
        dis.add( pout[ i ],-1 );
    }
    while( m-- ){
        char s[5];
        int  p, q, l, D1, D2;
        s[ 0 ] = gcha();
        switch( s[ 0 ] ){
            case 'Q':	p = read(); q = read();
                        l = lca( p , q );
                        D1 = dep[ p ] + dep[ q ] - 2 * dep[ l ];
                        D2 = dis.query( pin[ p ] ) + dis.query( pin[ q ] ) - 2 * dis.query( pin[ l ] );
                        if(D1 == D2) printf("Yes\n"); else printf("No\n"); break;
            case 'C':	p = read(); q = read();
                        if(dep[ p ] < dep[ q ]) p = q;
                        Q[ ++tot ] = p;
                        dis.add( pin[ p ], -1 );
                        dis.add( pout[ p ], 1 );
                        break;
            case 'U':	p = read();
                        p = Q[ p ];
                        dis.add( pin[ p ], 1 );
                        dis.add( pout[ p ], -1 );
                        break;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值