洛谷 U14475 部落冲突 【比赛】 【树链剖分 + 线段树】

时间:2023-03-10 06:12:36
洛谷 U14475 部落冲突 【比赛】  【树链剖分 + 线段树】

题目背景

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

洛谷 U14475 部落冲突 【比赛】  【树链剖分 + 线段树】

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

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

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

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

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

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

题目描述

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

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

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

每行一个“YesYesYes”或者“NoNoNo”,表示从第
ppp
个部落出发的建筑工人能否到达第 qqq
个部落

输入输出样例

输入样例#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

题解

下课后A完T1后离比赛结束还有30分钟,速码了160行,就因为一个变量重名没看出就没赶上提交QAQ

没关系。。还是A了

很中规中矩的一题树链剖分,查询树上两点间权值和【本题只需要检查是否为0就好】,没什么好讲的。
【大赞部落冲突O(∩_∩)O】


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define fo(i,x,y) for (int i = (x); i <= (y); i++)
#define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
using namespace std;
const int maxn = 300005,maxm = 600005,INF = 1000000000; inline int read(){
int out = 0,flag = 1;char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
return out * flag;
} int n,m; int head[maxn],nedge = 0;
struct EDGE{
int to,next;
}edge[maxm]; inline void build(int a,int b){
edge[nedge] = (EDGE){b,head[a]};
head[a] = nedge++;
edge[nedge] = (EDGE){a,head[b]};
head[b] = nedge++;
} void init(){
fill(head,head + maxn,-1);
n = read();
m = read();
for (int i = 1; i < n; i++) build(read(),read());
} int Siz[maxn],rt = 1,rmax = INF;
void dfs(int u,int f){
Siz[u] = 1;
int gmax = 1,to;
Redge(u)
if ((to = edge[k].to) != f){
dfs(to,u);
Siz[u] += Siz[to];
if (Siz[to] > gmax) gmax = Siz[to];
}
if (n - Siz[u] > gmax) gmax = n - Siz[u];
if (gmax < rmax){
rt = u;
rmax = gmax;
}
} int siz[maxn],top[maxn],fa[maxn],id[maxn],Hash[maxn],son[maxn],dep[maxn];
int cnt = 0; void dfs1(int u,int f,int d){
siz[u] = 1; fa[u] = f; dep[u] = ++d;
int to;
Redge(u){
if ((to = edge[k].to) != f){
dfs1(to,u,d);
siz[u] += siz[to];
if (!son[u] || siz[to] > siz[son[u]]) son[u] = to;
}
}
} void dfs2(int u,bool flag){
id[u] = ++cnt; Hash[cnt] = u;
top[u] = flag ? top[fa[u]] : u;
if (son[u]) dfs2(son[u],true);
int to;
Redge(u)
if ((to = edge[k].to) != fa[u] && to != son[u])
dfs2(to,false);
} int L,R,sum[4 * maxn]; void change(int u,int l,int r,int v){
if (l >= L && r <= R) sum[u] = v;
else {
int mid = (l + r) >> 1;
if (mid >= L) change(u<<1,l,mid,v);
else change(u<<1|1,mid + 1,r,v);
sum[u] = sum[u << 1] + sum[u << 1 | 1];
}
} int Query(int u,int l,int r){
if (l >= L && r <= R) return sum[u];
else {
int mid = l + r >> 1;
if (mid >= R) return Query(u<<1,l,mid);
else if (mid < L) return Query(u<<1|1,mid + 1,r);
else return Query(u<<1,l,mid) + Query(u<<1|1,mid + 1,r);
}
} void cal1(){
int u = read(),v = read(),ans = 0;
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u,v);
L = id[top[u]]; R = id[u];
ans += Query(1,1,n);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u,v);
L = id[u] + 1; R = id[v];
if (L <= R) ans += Query(1,1,n);
if (ans > 0) printf("No\n");
else printf("Yes\n");
} int N = 0,U[maxn]; void cal2(int p){
if (p > 0){
int u = read(),v = read();
if (fa[v] == u) swap(u,v);
U[++N] = u;
L = R = id[u];
change(1,1,n,p);
}
else {
int x = read();
L = R = id[U[x]];
change(1,1,n,p);
}
} void solve(){
char c;
while (m--){
c = getchar();
while (c != 'Q' && c != 'C' && c != 'U') c = getchar();
if (c == 'Q'){
cal1();
}else if (c == 'C'){
cal2(1);
}else {
cal2(0);
}
//L = 1; R = n; cout<<"1 to n : "<<Query(1,1,n)<<endl;
}
} int main()
{
init();
dfs(1,0);//cout<<rt<<endl;
dfs1(rt,0,0);
dfs2(rt,0);//REP(i,n) cout<<id[i]<<endl;
solve();
return 0;
}