【题目】
题目描述:
对于一个全集 U U U,对于他的两个子集 A , B A,B A,B,如果 A ⊂ B A\subset B A⊂B 或 B ⊂ A B⊂A B⊂A 或 A ∩ B = ∅ A∩B=\varnothing A∩B=∅,则这两个集合就是 U U U 的层流集。
现在小 Z 想把这个问题搬到树上来。他给出 n n n 个顶点的无根树,然后他把两个顶点 < u , v > <u,v> <u,v> 的简单路径所有的顶点加入一个集合,这样就会得到若干个集合。显然所有顶点构成的集合就是全集 U U U。他想知道他随手写出的若干组 < u , v > <u,v> <u,v> 构成的若干子集是否满足任意两个集合是 U U U 的层流集。
输入格式:
第一行是两个整数 n , m n,m n,m 表示有 n n n 个顶点, m m m 组子集。
接下来是 n − 1 n-1 n−1 行,每行 2 2 2 个整数 u , v u,v u,v,表示 u u u ~ v v v 有一条边(保证是一颗树)。
接下来 m m m 行,每行两个整数 u , v u,v u,v 表示由 u u u 到 v v v 的简单路径上顶点构成一个子集。
输出格式:
如果给出的 m m m 个子集任意两个子集满足是全集的层流集输出 “Yes” 否则输出 “No”。
样例数据:
输入
4 2
1 2
2 3
2 4
1 2
4 2
输出
No
提示:
子任务一(
5
5
5 分):
n
,
m
≤
15
n,m\le15
n,m≤15;
子任务二(
25
25
25 分):
n
,
m
≤
1000
n,m\le1000
n,m≤1000;
子任务三(
70
70
70 分):
n
,
m
≤
100000
n,m\le100000
n,m≤100000。
【分析】
先考虑怎么判断两个集合 A , B A,B A,B 是否是 U U U 的层流集。
如果存在两个集合 A , B A,B A,B ,使得 A , B A,B A,B 有交叉(此处的交叉定义为 A , B A,B A,B 有交集但是不存在包含关系),那就不满足任意两个集合都是 U U U 的层流集(也就是输出 “No”),如果不存在这样的一对集合,就输出 “Yes”。
我们将路径的长度从大到小排序,然后从大到小加边,每次判断之前的边与这条边是否有交叉。
怎么判断是否有交叉呢,不妨在每次加入一条边之后,对这条边上的点染上一个新颜色。判断交叉的时候,就看这条边上有几种颜色,如果有多于 1 1 1 种的颜色,就说明这条边与之前的边有交叉。
然后怎么判断是否只有 1 1 1 种颜色呢,可以记录下路径上的最大最小值,判断最大最小值是否相等即可。
可以自己画一下图来理解。
然后具体的实现可以看代码(虽然代码量巨大)。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,m,t,tot;
struct edge{int u,v,len;}e[N];
int first[N],v[N<<1],nxt[N<<1];
int Max[N<<2],Min[N<<2],cov[N<<2];
int fa[N],son[N],dep[N],Size[N],pos[N],top[N];
bool operator<(const edge &p,const edge &q){return p.len>q.len;}
void add(int x,int y)
{
nxt[++t]=first[x];
first[x]=t,v[t]=y;
}
void dfs1(int x)
{
int i,k;
Size[x]=1;
for(i=first[x];i;i=nxt[i])
{
k=v[i];
if(k==fa[x]) continue;
fa[k]=x,dep[k]=dep[x]+1;
dfs1(k),Size[x]+=Size[k];
if(Size[son[x]]<Size[k]) son[x]=k;
}
}
void dfs2(int x,int tp)
{
top[x]=tp,pos[x]=++tot;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i;i=nxt[i])
if(v[i]!=son[x]&&v[i]!=fa[x])
dfs2(v[i],v[i]);
}
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;
}
void pushup(int root)
{
Min[root]=min(Min[root<<1],Min[root<<1|1]);
Max[root]=max(Max[root<<1],Max[root<<1|1]);
}
void pushnow(int root,int k)
{
cov[root]=Min[root]=Max[root]=k;
}
void pushdown(int root,int l,int r,int mid)
{
if(!cov[root]) return;
pushnow(root<<1,cov[root]);
pushnow(root<<1|1,cov[root]);
cov[root]=0;
}
int queryMax(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y) return Max[root];
int mid=(l+r)>>1;pushdown(root,l,r,mid);
if(y<=mid) return queryMax(root<<1,l,mid,x,y);
if(x>mid) return queryMax(root<<1|1,mid+1,r,x,y);
return max(queryMax(root<<1,l,mid,x,y),queryMax(root<<1|1,mid+1,r,x,y));
}
int FindMax(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=max(ans,queryMax(1,1,n,pos[top[x]],pos[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans=max(ans,queryMax(1,1,n,pos[x],pos[y]));
return ans;
}
int queryMin(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y) return Min[root];
int mid=(l+r)>>1;pushdown(root,l,r,mid);
if(y<=mid) return queryMin(root<<1,l,mid,x,y);
if(x>mid) return queryMin(root<<1|1,mid+1,r,x,y);
return min(queryMin(root<<1,l,mid,x,y),queryMin(root<<1|1,mid+1,r,x,y));
}
int FindMin(int x,int y)
{
int ans=N;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
ans=min(ans,queryMin(1,1,n,pos[top[x]],pos[x]));
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
ans=min(ans,queryMin(1,1,n,pos[x],pos[y]));
return ans;
}
void Cover(int root,int l,int r,int x,int y,int k)
{
if(l>=x&&r<=y)
{
pushnow(root,k);
return;
}
int mid=(l+r)>>1;
if(x<=mid) Cover(root<<1,l,mid,x,y,k);
if(y>mid) Cover(root<<1|1,mid+1,r,x,y,k);
pushup(root);
}
void Modify(int x,int y,int k)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
Cover(1,1,n,pos[top[x]],pos[x],k);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
Cover(1,1,n,pos[x],pos[y],k);
}
int main()
{
int x,y,i,lca;
scanf("%d%d",&n,&m);
for(i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs1(1),dfs2(1,1);
for(i=1;i<=m;++i)
{
scanf("%d%d",&e[i].u,&e[i].v);
lca=LCA(e[i].u,e[i].v);
e[i].len=dep[e[i].u]+dep[e[i].v]-2*dep[lca];
}
sort(e+1,e+m+1);
for(i=1;i<=m;++i)
{
int maxn=FindMax(e[i].u,e[i].v);
int minn=FindMin(e[i].u,e[i].v);
if(maxn!=minn){puts("No");return 0;}
Modify(e[i].u,e[i].v,i);
}
puts("Yes");
return 0;
}