题目大意:有一棵
n
n
n 个结点的树,有
f
f
f 条简单路径,定义
f
[
i
]
f[i]
f[i] 为 第 i 条路径的点集,判断这
f
f
f 条路径任意两条是否满足:
1.
f
[
i
]
f[i]
f[i] 和
f
[
j
]
f[j]
f[j] 没有公共部分
2.
f
[
i
]
f[i]
f[i] 是
f
[
j
]
f[j]
f[j] 的子集
3.
f
[
j
]
f[j]
f[j] 是
f
[
i
]
f[i]
f[i] 的子集
可以转化为任意两条路径,要么不相交,要么包含
先预处理每条路径的长度,按它们的长度从大到小排序,这样处理的作用是当前枚举的路径不会包含其它路径,如果路径所在区间有其它路径,那么必然是被包含在其它路径内或者相交。
对路径区间进行染色,对每一条路径判断区间内是否只有一种颜色:即 m a x = = m i n max == min max==min,遇到不合法直接退出。
树上路径染色可以用树链剖分 + 线段树解决,预处理路径长度也可以用树链剖分寻找 lca。
总复杂度: n log 2 n n \log^2n nlog2n
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
vector<int> g[maxn];
int val[maxn << 2],mi[maxn << 2],laze[maxn << 2];
int n,f;
int deep[maxn],son[maxn],sz[maxn],p[maxn],top[maxn],dfn[maxn],idfn[maxn],cnt; //树链剖分
int pos[maxn];
struct node{
int x,y,l;
node(int xi = 0,int yi = 0,int li = 0) {
x = xi;
y = yi;
l = li;
}
bool operator < (const node & rhs) const {
return l > rhs.l;
}
}a[maxn];
void pushdown(int rt) {
if(!laze[rt]) return;
val[rt << 1] = mi[rt << 1] = laze[rt << 1] = laze[rt];
val[rt << 1 | 1] = mi[rt << 1 | 1] = laze[rt << 1 | 1] = laze[rt];
laze[rt] = 0;
}
void build(int rt,int l,int r) {
val[rt] = mi[rt] = 0;
if(l == r) return ;
int mid = l + r >> 1;
build(lson);build(rson);
}
int qry_mx(int L,int R,int rt,int l,int r) {
if(L <= l && r <= R) {
return val[rt];
}
pushdown(rt);
int mid = l + r >> 1;
int sum = 0;
if(L <= mid) sum = max(qry_mx(L,R,lson),sum);
if(mid + 1 <= R) sum = max(qry_mx(L,R,rson),sum);
return sum;
}
int qry_mi(int L,int R,int rt,int l,int r) {
if(L <= l && r <= R) {
return mi[rt];
}
pushdown(rt);
int mid = l + r >> 1;
int sum = 1e9;
if(L <= mid) sum = min(qry_mi(L,R,lson),sum);
if(mid + 1 <= R) sum = min(qry_mi(L,R,rson),sum);
return sum;
}
void upd(int L,int R,int v,int rt,int l,int r) {
if(L <= l && r <= R) {
laze[rt] = val[rt] = mi[rt] = v;
return ;
}
pushdown(rt);
int mid = l + r >> 1;
if(L <= mid) upd(L,R,v,lson);
if(mid + 1 <= R) upd(L,R,v,rson);
val[rt] = max(val[rt << 1],val[rt << 1 | 1]);
mi[rt] = min(mi[rt << 1],mi[rt << 1 | 1]);
}
void prework(int u,int fa) {
p[u] = fa,sz[u] = 1,son[u] = 0;
for(auto it : g[u]) {
if(it == fa) continue;
deep[it] = deep[u] + 1;
prework(it,u);
sz[u] += sz[it];
if(!son[u] || sz[it] > sz[son[u]]) son[u] = it;
}
}
void dfs(int u,int fa,int tp) {
dfn[u] = ++cnt;idfn[cnt] = u;
top[u] = tp;
if(son[u]) dfs(son[u],u,tp);
for(auto it : g[u]) {
if(it == fa || it == son[u]) continue;
dfs(it,u,it);
}
}
bool ask(int u,int v) {
int mx = 0,mi = 1e9;
while(top[u] != top[v]) {
if(deep[top[u]] < deep[top[v]]) swap(u,v);
mx = max(qry_mx(dfn[top[u]],dfn[u],1,1,n),mx);
mi = min(qry_mi(dfn[top[u]],dfn[u],1,1,n),mi);
u = p[top[u]];
}
if(deep[u] > deep[v]) swap(u,v);
mx = max(mx,qry_mx(dfn[u],dfn[v],1,1,n));
mi = min(mi,qry_mi(dfn[u],dfn[v],1,1,n));
return mx == mi;
}
void modify(int u,int v,int k) {
while(top[u] != top[v]) {
if(deep[top[u]] < deep[top[v]]) swap(u,v);
upd(dfn[top[u]],dfn[u],k,1,1,n);
u = p[top[u]];
}
if(deep[u] > deep[v]) swap(u,v);
upd(dfn[u],dfn[v],k,1,1,n);
}
int find(int u,int v) {
while(top[u] != top[v]) {
if(deep[top[u]] < deep[top[v]]) swap(u,v);
u = p[top[u]];
}
if(deep[u] > deep[v]) swap(u,v);
return u;
}
int main() {
scanf("%d%d",&n,&f);
for(int i = 1; i < n; i++) {
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
build(1,1,n);
prework(1,0);dfs(1,0,1);
for(int i = 1; i <= f; i++) {
int u,v;scanf("%d%d",&u,&v);
a[i] = node(u,v,0);
int f = find(u,v);
a[i].l = deep[u] + deep[v] - 2 * deep[f] + 1;
}
sort(a + 1,a + f + 1);
bool tmp = true;
for(int i = 1; i <= f; i++) {
int u = a[i].x,v = a[i].y;
tmp = ask(u,v);
if(!tmp) break;
modify(u,v,i);
}
if(tmp) puts("Yes");
else puts("No");
return 0;
}