题目背景
在一个叫做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 - 1n−1 行,每行两个数 pp , qq ,代表了第 pp 个部落与第 qq 个部落之间有一条道路相连
接下来的 mm 行,每行表示一件事,详见题目描述
输出格式:
每行一个“ YesYes ”或者“ NoNo ”,表示从第 pp 个部落出发的建筑工人能否到达第 qq 个部落
输入输出样例
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
Yes
No
No
No
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
Yes
No
No
Yes
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
Yes
Yes
Yes
Yes
No
说明
对于30%的数据 1<=n,m<=6000
对于另30%的数据,保证部落之间的地理关系是一条链,且 i 与 i + 1 之间有一条道路
对于另30%的数据,1<=n,m<=100000
对于100%的数据,1<=n,m<=300000
题解
话说没想到这题思路这么妙我居然没想到orz
个人理解:可以将树进行括号表示,用树剖+树状数组可以维护,同时可以求出某一个点的深度
如果某一条边断了,在树状数组上把深度更深的点的括号表示的影响消去
这样的话,同时用深度查询和括号序列查询两点之间的距离,因为断边的影响已被消去,所以如果路径上有断边,二者的查询结果必不相同
于是可以判断能否到达了
//minamoto
#include<bits/stdc++.h>
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
char obuf[<<],*o=obuf;
inline void print(int x){
x?(*o++='Y',*o++='e',*o++='s'):(*o++='N',*o++='o');
*o++='\n';
}
const int N=;
int st[N],h,ls[N],rs[N],ver[N<<],Next[N<<],head[N],tot,s[N<<],cnt,n,m;
inline void add(int p,int c){
for(int i=p;i<=n<<;i+=i&(-i)) s[i]+=c;
}
inline int sum(int p){
int res=;
for(int i=p;i;i-=i&(-i)) res+=s[i];
return res;
}
inline void add_(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
}
int sz[N],fa[N],son[N],dep[N],top[N];
void dfs(int u,int f){
sz[u]=,dep[u]=dep[fa[u]]+;
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==f) continue;
fa[v]=u;
dfs(v,u);
sz[u]+=sz[v];
if(!son[u]||sz[v]>sz[son[u]]) son[u]=v;
}
}
void dfs(int u){
if(!top[u]) top[u]=u;
ls[u]=++cnt;
if(son[u]) top[son[u]]=top[u],dfs(son[u]);
for(int i=head[u];i;i=Next[i]){
int v=ver[i];
if(v==fa[u]||v==son[u]) continue;
dfs(v);
}
rs[u]=++cnt;
}
int LCA(int x,int y){
while(top[x]!=top[y])
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
int main(){
//freopen("testdata.in","r",stdin);
n=read(),m=read();
for(int i=;i<n;++i){
int u=read(),v=read();add_(u,v);
}
dfs(n>>,),dfs(n>>);
for(int i=;i<=n;++i) add(ls[i],),add(rs[i],-);
while(m--){
char ch;
while((ch=getc())!='C'&&ch!='Q'&&ch!='U');
switch(ch){
case 'Q':{
int a=read(),b=read();
int lca=LCA(a,b);
int d=dep[a]+dep[b]-*dep[lca];
int dd=sum(ls[a])+sum(ls[b])-*sum(ls[lca]);
print(d==dd);
break;
}
case 'C':{
int a=read(),b=read();
dep[b]>dep[a]?a=b:;
st[++h]=a;
add(ls[a],-),add(rs[a],);
break;
}
case 'U':{
int a=read();
a=st[a];
add(ls[a],),add(rs[a],-);
break;
}
}
}
fwrite(obuf,o-obuf,,stdout);
return ;
}