【题目描述】
在一个叫做
Travian
T
r
a
v
i
a
n
的世界里,生活着各个大大小小的部落。其中最为强大的是罗马、高卢和日耳曼。他们之间为了争夺资源和土地,进行了无数次的战斗。期间诞生了众多家喻户晓的英雄人物,也留下了许多可歌可泣的动人故事。
其中,在大大小小的部落之间,会有一些道路相连,这些道路是 Travian T r a v i a n 世界里的重要枢纽,简单起见,你可以把这些部落与部落之间相连的道路看作一颗树,可见每条道路对于 Travian T r a v i a n 世界的重要程度。有了这些道路,建筑工人就可以通过这些道路进行友好外交啦。
然而,事情并不会像想象的那样美好,由于资源的匮乏,相邻的部落(由一条道路相连的部落)之间经常会发生大大小小的冲突事件,更有甚者,会升级为部落之间的大型战争。
为了避免误伤,每当两个相邻的部落之间发生大型战争之时,这两个部落间的道路是不允许通行的,对于一些强大的部落,甚至能与多个相邻的部落同时开战,同样的,这些战争地带的道路十分危险,是不可通行的。
天下之势,分久必合,当两个部落经历了不打不相识的苦战之后,他们可以签订停战协议(暂时停战,以后依旧可能再次开战),这样,两个部落之间的道路又会重新恢复为可通行状态,建筑工人们又可以经过此地购买最新的大本营设计图纸来强大自己的部落了。
为了简单起见,我们把各大战争事件按发起的时间顺序依次编号(最先发起的战争编号就为 1,第二次战争编号就为 2,以此类推),当两个部落停战之时,则会直接告诉你这场战争的编号,然后这场战争就载入了史册,不复存在了,当然,这并不会影响到其他战争的编号。
建筑工人十分讨厌战争,因为战争,想从一个部落到另一个部落进行友好交流的建筑工人可能就此白跑一趟。所以,在他们出发之前,都会向你问问能不能到达他们想去的部落。
简单起见,你就是要处理下面三件事,所有的事件都是按照时间顺序给出的。
1. Qpq Q p q 从第 p p 个部落出发的建筑工人想知道能否到达第个部落了,你要回答的便是 Yes/No Y e s / N o ,注意大小写
2. Cpq C p q 第 p p 个部落与第个部落开战了,保证他们一定是相邻的部落,且目前处于停战(未开战)状态
3. Ux U x 第 x x 次发生的战争结束了,它将永远的被载入史册,不复存在(保证这个消息不会告诉你多次)
【输入格式】
第一行两个数和
m
m
,代表了一共有
n
n
个部落,代表了以上三种事件发生的总数
接下来的 n−1 n − 1 行,每行两个数 p p , ,代表了第 p p 个部落与第个部落之间有一条道路相连
接下来的 m m 行,每行表示一件事,详见题目描述
【输出格式】
每行一个“”或者“
No
N
o
”,表示从第
p
p
个部落出发的建筑工人能否到达第个部落
SampleInput S a m p l e I n p u t
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
SampleOutput S a m p l e O u t p u t
Yes
Yes
Yes
Yes
No
【题意分析】
图已盗(小声bb,另外,部落冲突真的炒鸡好玩!!!!
对于这道题,蒟蒻第一反应是树剖,于是上周就打了个树剖上去,然后蜜汁WA了,只有30分。
至今百思不得其解
然后这周末心血来潮学了 LCT L C T ,然后再看这道题。。。
就是 LCT L C T 裸题啊,输入边的时候 link l i n k ,开战就 cut c u t ,休战再 link l i n k ,询问的时候两次 findroot f i n d r o o t ,看是不是在一个 Splay S p l a y 里。
简直裸题。。。太简单了
Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAXN 400000
using namespace std;
struct WarCraft{
int Attack,Defend;
}Battle[MAXN];
//记录战争信息
int father[MAXN],son[MAXN][2],stack[MAXN],lazy[MAXN];
int n,q,tot;
namespace LCT{
inline bool isroot (int x){
return ! (son[father[x]][0] == x || son[father[x]][1] == x);
}//是否为根丫
inline void pushdown (int x){
if (lazy[x]){
swap (son[x][0],son[x][1]);
lazy[son[x][0]] ^= 1;
lazy[son[x][1]] ^= 1;
lazy[x] = 0;
}
}//下传旋转标记
inline void rotate (int x){
int y = father[x],z = father[y];
int k = son[y][1] == x,kk = son[z][1] == y;
if (!isroot (y))son[z][kk] = x;
father[x] = z;
son[y][k] = son[x][k^1];
father[son[x][k^1]] = y;
son[x][k^1] = y;
father[y] = x;
}//转圈圈
inline void splay (int x){
int top = 0;
stack[++top] = x;
for (register int i = x;!isroot (i);i = father[i])
stack[++top] = father[i];
for (register int i = top;i;i--)
pushdown (stack[i]);
while (!isroot (x)){
int y = father[x],z = father[y];
if (!isroot (y))
(son[y][1] == x) ^ (son[z][1] == y)
?rotate (x) : rotate (y);
rotate (x);
}
}//伸展维护平衡树
inline void access (int x){
for (register int y = 0;x;x = father[y = x]){
splay(x);
son[x][1] = y;
}
}//打通根节点到x路径
inline void makeroot (int x){
access (x);
splay (x);
lazy[x] ^= 1;
}//换根
inline int findroot (int x){
access (x);
splay (x);
pushdown (x);
while (son[x][0]){
pushdown (x);
x = son[x][0];
}
return x;
}//找真正的根
inline void link (int x,int y){
makeroot (x);
father[x] = y;
}//连
inline void cut (int x,int y){
makeroot (x);
access (y);
splay (y);
father[x] = son[y][0] = 0;
}//切
inline void query (int x,int y){
(findroot (x) == findroot (y))
? puts ("Yes") : puts ("No");
}//询问
}
int main (){
scanf ("%d%d",&n,&q);
for (register int i = 1;i < n;i++){
int x,y;
scanf ("%d%d",&x,&y);
LCT :: link (x,y);
}
while (q--){
char s[5];
scanf ("%s",s);
if (s[0] == 'U'){
int x;
scanf ("%d",&x);
LCT :: link (Battle[x].Attack,Battle[x].Defend);
//将停战的两个点连起来
}
if (s[0] == 'C'){
int x,y;
scanf ("%d%d",&x,&y);
Battle[++tot].Attack = x;
Battle[tot].Defend = y;
LCT :: cut (x,y);
//打架了,就断开
}
if (s[0] == 'Q'){
int x,y;
scanf ("%d%d",&x,&y);
LCT :: query (x,y);
//询问
}
}
return 0;
}