Cogs 2856. [洛谷U14475]部落冲突
2856. [洛谷U14475]部落冲突
★★★ 输入文件:lct.in
输出文件:lct.out
简单对比
时间限制:1 s 内存限制:256 MB
【题目描述】
在一个叫做Travian的世界里,生活着各个大大小小的部落。其中最为强大的是罗马、高卢和日耳曼。他们之间为了争夺资源和土地,进行了无数次的战斗。期间诞生了众多家喻户晓的英雄人物,也留下了许多可歌可泣的动人故事。
其中,在大大小小的部落之间,会有一些道路相连,这些道路是Travian世界里的重要枢纽,简单起见,你可以把这些部落与部落之间相连的道路看作一颗树,可见每条道路对于Travian世界的重要程度。有了这些道路,建筑工人就可以通过这些道路进行友好外交啦。
然而,事情并不会像想象的那样美好,由于资源的匮乏,相邻的部落(由一条道路相连的部落)之间经常会发生大大小小的冲突事件,更有甚者,会升级为部落之间的大型战争。
为了避免误伤,每当两个相邻的部落之间发生大型战争之时,这两个部落间的道路是不允许通行的,对于一些强大的部落,甚至能与多个相邻的部落同时开战,同样的,这些战争地带的道路十分危险,是不可通行的。
天下之势,分久必合,当两个部落经历了不打不相识的苦战之后,他们可以签订停战协议(暂时停战,以后依旧可能再次开战),这样,两个部落之间的道路又会重新恢复为可通行状态,建筑工人们又可以经过此地购买最新的大本营设计图纸来强大自己的部落了。
为了简单起见,我们把各大战争事件按发起的时间顺序依次编号(最先发起的战争编号就为 1,第二次战争编号就为 2,以此类推),当两个部落停战之时,则会直接告诉你这场战争的编号,然后这场战争就载入了史册,不复存在了,当然,这并不会影响到其他战争的编号。
建筑工人十分讨厌战争,因为战争,想从一个部落到另一个部落进行友好交流的建筑工人可能就此白跑一趟。所以,在他们出发之前,都会向你问问能不能到达他们想去的部落。
【输入格式】
第一行两个数 n 和 m, n 代表了一共有 n 个部落,m 代表了以上三种事件发生的总数
接下来的 n - 1行,每行两个数 p , q,代表了第 p 个部落与第 q个部落之间有一条道路相连
接下来的 m 行,每行表示一件事,详见题目描述
【输出格式】
每行一个“Yes”或者“No”,表示从第 p个部落出发的建筑工人能否到达第 q个部落
【样例输入】
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
【提示】
对于30%的数据 1<=n,m<=6000
对于另30%的数据,保证部落之间的地理关系是一条链,且 i 与 i + 1 之间有一条道路
对于另30%的数据,1<=n,m<=100000
对于100%的数据,1<=n,m<=300000
【来源】
NOIP 模拟赛 by WISCO信息组
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #define maxn 300010 using namespace std; int n,m,num,head[maxn],s[maxn],t[maxn],cnt,fa[maxn],dep[maxn],edge[maxn]; int son[maxn],top[maxn],sz[maxn]; struct node{ int to,pre,v; }e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } char ch[5]; void dfs(int now,int father){ fa[now]=father; dep[now]=dep[father]+1; sz[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; dfs(to,now); sz[now]+=sz[to]; edge[to]=i; if(!son[now]||sz[son[now]]<sz[to])son[now]=to; } } void dfs2(int now,int father){ top[now]=father; if(son[now])dfs2(son[now],father); for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==son[now]||to==fa[now])continue; dfs2(to,to); } } int qlca(int a,int b){ while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); a=fa[top[a]]; } if(dep[a]>dep[b])swap(a,b); return a; } void change(int a,int b,int v){ int lca=qlca(a,b); while(1){ if(a==lca)break; e[edge[a]].v+=v; a=fa[a]; } while(1){ if(b==lca)break; e[edge[b]].v+=v; b=fa[b]; } } int query(int a,int b){ int res=0; int lca=qlca(a,b); while(1){ if(a==lca)break; res=max(res,e[edge[a]].v); a=fa[a]; } while(1){ if(b==lca)break; res=max(res,e[edge[b]].v); b=fa[b]; } return res; } int main(){ // freopen("Cola.txt","r",stdin); freopen("lct.in","r",stdin);freopen("lct.out","w",stdout); scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); Insert(x,y);Insert(y,x); } dfs(1,0); dfs2(1,1); for(int i=1;i<=m;i++){ scanf("%s",ch); if(ch[0]=='Q'){ scanf("%d%d",&x,&y); int flag=query(x,y); if(flag)puts("No"); else puts("Yes"); } if(ch[0]=='C'){ scanf("%d%d",&x,&y); cnt++;s[cnt]=x;t[cnt]=y; change(x,y,1); } if(ch[0]=='U'){ scanf("%d",&z); x=s[z];y=t[z]; change(x,y,-1); } } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 树剖维护树上的两种操作: 1.修改两点之间所有边的边权 2.询问两点之间边权总和是否为0 */ #include <iostream> #include <cstdio> #include <algorithm> #include <vector> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=300010; int n,m; struct ww{int u,v;}war[maxn]; int wartot; vector<int>t[maxn]; int tot,sz[maxn],son[maxn],fa[maxn],dep[maxn],top[maxn],id[maxn],pre[maxn]; inline void dfs1(int u,int f,int d){ sz[u]=1;dep[u]=d;fa[u]=f; int len=t[u].size(); for(int i=0;i<len;i++){ int v=t[u][i]; if(v==f)continue; dfs1(v,u,d+1); sz[u]+=sz[v]; if(!son[u]||sz[son[u]]<sz[v])son[u]=v; } } inline void dfs2(int u,int tou){ top[u]=tou;id[u]=++tot;pre[tot]=u; if(!son[u])return; dfs2(son[u],tou); int len=t[u].size(); for(int i=0;i<len;i++){ int v=t[u][i]; if(v!=fa[u]&&v!=son[u])dfs2(v,v); } } int sum[maxn<<2]; inline void change(int o,int l,int r,int nl,int nr,int v){ if(l>=nl&&r<=nr){ sum[o]+=v; return; } int m=(l+r)>>1,ls=o<<1,rs=ls|1; if(m>=nl)change(ls,l,m,nl,nr,v); if(m<nr)change(rs,m+1,r,nl,nr,v); sum[o]=sum[ls]+sum[rs]; } inline int find(int o,int l,int r,int nl,int nr){ if(l>=nl&&r<=nr)return sum[o]; int m=(l+r)>>1,ls=o<<1,rs=ls|1; int ans=0; if(m>=nl)ans+=find(ls,l,m,nl,nr); if(m<nr)ans+=find(rs,m+1,r,nl,nr); return ans; } inline void ok(int x,int y){ int u=top[x],v=top[y]; int ans=0; while(u!=v){ if(dep[u]<dep[v])swap(u,v),swap(x,y); ans+=find(1,1,tot,id[u],id[x]); x=fa[u];u=top[x]; } if(dep[x]>dep[y])swap(x,y); if(x!=y)//以边树剖时加特判。 ans+=find(1,1,tot,id[son[x]],id[y]); if(ans)puts("No"); else puts("Yes"); } int main(){ freopen("lct.in","r",stdin); freopen("lct.out","w",stdout); n=read();m=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); t[u].push_back(v); t[v].push_back(u); } dfs1(1,0,1); dfs2(1,1); for(int i=1;i<=m;i++){ char c=getchar(); while(c!='Q'&&c!='C'&&c!='U')c=getchar(); if(c=='Q'){ int x=read(),y=read(); ok(x,y); } if(c=='C'){ int ru=1; war[++wartot].u=read(),war[wartot].v=read(); int to; if(war[wartot].u>war[wartot].v)to=war[wartot].u; else to=war[wartot].v; change(1,1,tot,id[to],id[to],ru); } if(c=='U'){ int ru=-1; int x=read(); int to; if(war[x].u>war[x].v)to=war[x].u; else to=war[x].v; change(1,1,tot,id[to],id[to],ru); } } return 0; }