CF901D Weighting a Tree

一、题目

点此看题

二、解法

从问题的简单情形开始考虑,如果无向图是一棵树怎么办?我们可以从叶子往上构造,要让叶子合法边的权值只有一种可能,所以最后我们能让除了根的所有点都一定合法。

那么扩展到图上,我们可以找出原图的一棵 \(\tt dfs\) 树,然后把非树边的边权赋值成 \(0\),按树的方法做就只有根的问题需要解决了。然后我们使用调整法,也就是调整非树边的边权让根也合法。

调整多条边是很困难的事,所以现在我们就开始找结论吧,首先考虑让答案合法的必要条件是现在的权值是偶数。然后我们着重从非树边构成环的奇偶性来考虑,考虑偶环是没有用的,因为它不能把修改的点权集中在一个点上,所以调整偶环是没有作用的,我们忽略它。

再考虑调整奇环,如果非树边设置的权值是 \(x\),首先我们要让换上的点合法,那么环根的点权会变化 \(2x/-2x\),我们再把这个影响传递到根上面,那么调整后根会相应的变化 \(2x/-2x\)(取决于根到环的距离),这说明只要存在奇环就是有解的,调整只需要用到这条非树边。

最后说一下实现的细节,我们可以把奇环的环根当成树根建树,这样调整起来会方便一些。

三、总结

很多变元的构造题中,可以只考虑少部分的变元就能给出构造方案。

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
#define int long long 
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,rt,nw,id,a[M],b[M],dep[M],fa[M],pr[M],pid[M];
struct node {int v,id;};vector<node> g[M];
void dfs1(int u,int p)
{
	dep[u]=dep[fa[u]=p]+1;
	for(auto x:g[u]) if(p!=x.v)
	{
		if(!nw && dep[x.v] && (dep[u]-dep[x.v])%2==0)
		{
			rt=u;nw=x.v;id=x.id;
			for(int i=u;i!=x.v;i=fa[i]) pr[i]=fa[i];
		}
		if(!dep[x.v]) dfs1(x.v,u);
	}
}
void dfs2(int u,int p)
{
	dep[u]=dep[fa[u]=p]+1;
	for(int t=0;t<2;t++) for(auto x:g[u])
		if((t || x.v==pr[u]) && !dep[x.v] && x.id!=id)
			pid[x.v]=x.id,dfs2(x.v,u);
	a[fa[u]]-=(b[pid[u]]=a[u]);
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g[u].push_back(node{v,i});
		g[v].push_back(node{u,i});
	}
	rt=1;dfs1(1,0);
	memset(dep,0,sizeof dep);
	dfs2(rt,0);
	if(nw)
	{
		int d=a[rt]/2;a[rt]%=2;b[id]+=d;
		for(int fl=-d;nw!=rt;fl=-fl)
			b[pid[nw]]+=fl,nw=fa[nw];
	}
	if(a[rt]) {puts("NO");return 0;}
	puts("YES");
	for(int i=1;i<=m;i++) printf("%lld\n",b[i]);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15811525.html