[BalticOI 2021] The Xana coup (Day2)

巳时,闲来无事,学分数规划至正午,忽觉无用,遂大悲,阅洛谷题库以寻题,钦四五题以切,遇此好题。

题意。

一棵 \(N\) 个点的树,点权不是 0 就是 1。

我们有一个神秘的操作,我们先选择一个点,把它和所有的相邻点权都取反。

询问最小的操作次数,使得所有的点权最后都为 0。

\(N\)\(1e5\) 级别

做法

如果思考这个问题的特点,我们会发现这个东西跟顺序是没有关系的,因为我们如果确定了某个点的操作次数,什么时候进行显然都是一样的。

于是我们可以通过子树合并的顺序进行,每次对父亲产生的约束仅仅会向上一层,所以我们可以尝试使用树形dp.

怎么设计状态?不难想到设 dp[u][0/1] 代表 \(u\) 子树,不会/会 影响到父亲的,保证树中清空的最小操作次数。

然而这个样子我们似乎并没办法去进行转移,原因是我们不清楚何时进行操作,我们的转移一定是基于我们进行的操作的。

既然不知道,我们便可以把这个东西设进状态。

可以看出,一个点操作偶数次相当于没操作,所以我们加一维 0/1 表示这个点经历了若干次操作 没有/有 被改变。

对应的,dp 数组定义中需要加上所清空范围不包含 u 节点。

理论上应该是能做了,先试着列一下转移式子,有需要再进行添加。

\(v \in son[u]\),我们扫到一个子树,无非就是受不受影响,把这两种情况列出来就好了。

\(dp_{u,0,0}\gets min_{v\in son[u]}(dp_{v,0,a_v}+dp_{u,0,0},dp_{v,1,a[v]}+dp_{u,0,1})\)

前者是不受影响,后者是受影响。

同理我们接着列就行了。

\(dp_{u,0,1}\gets min_{v\in son[u]}(dp_{v,0,a_v}+dp_{u,0,1},dp_{v,1,a[v]}+dp_{u,0,0})\)

\(dp_{u,1,0}\gets min_{v\in son[u]}(dp_{v,0,(1-a_v)}+dp_{u,1,0},dp_{v,1,(1-a[v])}+dp_{u,1,1})\)

\(dp_{u,1,1}\gets min_{v\in son[u]}(dp_{v,0,(1-a_v)}+dp_{u,1,1},dp_{v,1,(1-a[v])}+dp_{u,1,0})\)

打比较长的 Latex 能力较弱,应该没什么大问题了。

考虑怎么初始化,实际很简单,初始化什么都不动和修改一下就行,其它情况等着转移上来。

就是 dp[u][0][0]=0, dp[u][1][1]=1, dp[u][1][0]=dp[u][0][1]=0x3f3f3f3f;

之后还需要注意我们的转移可能会出现问题,直接这么写会出环。

每一次进行转移的时候我们使用 tmp[2][2] 存一下 dp[u][2][2] 的值,里边用这个进行转移就没问题了。

代码↓

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN=1e6+116;
struct Node{
	int nxt, to;
}node[MN];
int head[MN], tottt;
void insert(int u, int v){
	node[++tottt].to=v;
	node[tottt].nxt=head[u];
	head[u]=tottt; return;
}
int a[MN], n, dp[MN][2][2], ans;
void dfs(int u, int father){
	bool isleaf=true;
	dp[u][0][0]=0; dp[u][1][1]=1;
	dp[u][1][0]=1e10;
	dp[u][0][1]=1e10;
	for(int i=head[u];i;i=node[i].nxt){
		int v=node[i].to;
		if(v==father) continue;
		dfs(v,u); isleaf=false;
	}
	for(int i=head[u];i;i=node[i].nxt){
		int v=node[i].to;
		if(v==father) continue;
		int tmp[2][2];
		tmp[0][0]=dp[u][0][0];
		tmp[0][1]=dp[u][0][1];
		tmp[1][1]=dp[u][1][1];
		tmp[1][0]=dp[u][1][0];
		dp[u][0][0]=min(tmp[0][0]+dp[v][0][a[v]],tmp[0][1]+dp[v][1][a[v]]);
		dp[u][0][1]=min(tmp[0][1]+dp[v][0][a[v]],tmp[0][0]+dp[v][1][a[v]]);
		dp[u][1][0]=min(tmp[1][0]+dp[v][0][(1-a[v])],tmp[1][1]+dp[v][1][(1-a[v])]);
		dp[u][1][1]=min(tmp[1][1]+dp[v][0][(1-a[v])],tmp[1][0]+dp[v][1][(1-a[v])]);
	}
	if(u==1){
		ans=min(dp[1][0][a[1]],dp[1][1][a[1]]);
	}
}
signed main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n;
	for(int i=1,u,v; i<n; ++i){
		cin>>u>>v; insert(u,v); insert(v,u);
	}
	for(int i=1; i<=n; ++i) cin>>a[i];
	dfs(1,1);
	if(ans>=1e10) cout<<"impossible\n";
	else cout<<ans<<'\n';
	return 0;
}
posted @ 2025-09-17 18:50  BaiBaiShaFeng  阅读(10)  评论(0)    收藏  举报
Sakana Widget右下角定位