gym 101630 L - Laminar Family(树上区间染色 + 树链剖分)

题目大意:有一棵 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值