*题解:CF1904F Beautiful Tree

题目链接

解析

阅读完题面不难联想到差分约束,让值小的点向值大的点连边,问题在于此题涉及到单点向多点连边以及多点向单点连边。

考虑倍增优化建图,预处理出编号 \(f_{i,j}\) 表示从点 \(i\)\(i\)\(2^j\) 级祖先(不含)的所有点,并令 \(f_{i,j}\)\(f_{i,j - 1}\)\(f_{fa_{i,j - 1},j - 1}\) 连边,这样就可以将约束关系发散下去,便于处理单点向多点连边。同理,要想处理多点向单点连边,就需要反着来建图,令 \(g_{i,j - 1}\)\(g_{fa_{i,j - 1},j - 1}\)\(g_{i,j}\) 连边,以将约束关系汇集起来。

回到此题,只需将从 \(a\)\(b\) 的路径根据 \(c\) 和 LCA 来分割,分段处理连边即可。

时间复杂度 \(O((n + m)\log n)\)

代码

#include<bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pii;
const int N = 2e5 + 5,M = 20;
vector<int> t[N];
int fa[N][M],num1[N][M],num2[N][M],dep[N],ind[N * M * 2];
int res[N];
int cnt;
int head[N * M * 2],to[N * M * 6],nxt[N * M * 6];
int ecnt;
void add(int a,int b){
	ecnt++;
	nxt[ecnt] = head[a];
	to[ecnt] = b;
	head[a] = ecnt;
}
void dfs(int x,int f){
	fa[x][0] = f;
	dep[x] = dep[f] + 1;
	num1[x][0] = ++cnt;
	add(num1[x][0],x);
	ind[x]++;
	num2[x][0] = ++cnt;
	add(x,num2[x][0]);
	ind[num2[x][0]]++;
	for(int i=1;i<M;i++){
		fa[x][i] = fa[fa[x][i - 1]][i - 1];
		if(dep[x] - (1 << i) + 1 >= 1){
			num1[x][i] = ++cnt;
			num2[x][i] = ++cnt;
			add(num1[x][i],num1[x][i - 1]);
			ind[num1[x][i - 1]]++;
			add(num2[x][i - 1],num2[x][i]);
			ind[num2[x][i]]++;
		}
		if(dep[x] - (1 << i) + 1 >= 1 && num1[fa[x][i - 1]][i - 1] != 0){
			add(num1[x][i],num1[fa[x][i - 1]][i - 1]);
			ind[num1[fa[x][i - 1]][i - 1]]++;
			add(num2[fa[x][i - 1]][i - 1],num2[x][i]);
			ind[num2[x][i]]++;
		}	
	}
	for(int nx : t[x])if(nx != f){
		dfs(nx,x);
	}
}
int get(int a,int b){
	if(dep[a] > dep[b]) swap(a,b);
	for(int i=M - 2;i>=0;i--){
		if(dep[fa[b][i]] >= dep[a]){
			b = fa[b][i];
		}
	}
	if(a == b) return a;
	for(int i=M - 2;i>=0;i--){
		if(fa[a][i] != fa[b][i]){
			a = fa[a][i];
			b = fa[b][i];
		}
	}
	return fa[a][0];
}
void build(int a,int b,int c,int t){
	for(int i=M - 2;i>=0;i--){
		if(dep[fa[a][i]] >= dep[b]){
			if(t == 1){
				add(c,num1[a][i]); 
				ind[num1[a][i]]++;
			}else{
				add(num2[a][i],c);
				ind[c]++;
			}
			a = fa[a][i];
		}
	}
}
int read(){
	char c = getchar();
	int a = 1,x = 0;
	while(c > '9' || c < '0'){
		c = getchar();
	}
	while(c >= '0' && c <= '9'){
		x = x * 10 + c - '0';
		c = getchar();
	}
	return a * x;
}
int main(){
//	ios::sync_with_stdio(false);
//	cin.tie(0);	
//	freopen("in.txt","r",stdin);
//	freopen("out1.txt","w",stdout);
	int n = read(),m = read();
	cnt = n;
	for(int i=1;i<n;i++){
		int u = read(),v = read();
		t[u].push_back(v);
		t[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=m;i++){
		int t = read(),a = read(),b = read(),c = read();	
		int lca = get(a,b);
		if(get(b,c) == c){
			swap(a,b);
		}
		build(a,c,c,t);
		if(c != lca){
			build(fa[c][0],lca,c,t);
			if(t == 1){
				add(c,lca);
				ind[lca]++;
			}else{
				add(lca,c);
				ind[c]++;
			}
		}
		build(b,lca,c,t);
	}
	queue<int> q;
	for(int i=1;i<=cnt;i++){
		if(!ind[i]){
			q.push(i);
		}
	}
	int now = 1;
	while(!q.empty()){
		int u = q.front();
		q.pop(); 
		cnt--;
		if(u <= n){
			res[u] = now;
			now++;
		}
		for(int i = head[u];i;i = nxt[i]){
			int v = to[i];
			ind[v]--;
			if(!ind[v]){
				q.push(v);
			}
		}
	}
	if(cnt){
		cout<<-1;
	}else{
		for(int i=1;i<=n;i++){
			cout<<res[i]<<" ";
		}
	}
	return 0;
}
posted @ 2026-09-01 21:18  yutar  阅读(5)  评论(0)    收藏  举报