CF840B Leha and another game about graph

简化题意

有一个联通无向图,每个点有要求:度必须是偶数、必须是奇数或无限制。
求是否能找出一个包含所有点的子图满足所有点的要求,若能,输出这个子图的边集

思路

首先,看到无向连通图图上的问题,一个方向就是建DFS生成树
将图中的边转化为树边和非树边,非树边对答案的影响转化为生成树上的覆盖。

然而这道题简单到甚至不用考虑非树边....

然后我们会想到利用树的性质:子树独立。所以可以考虑如何把一个子树内的点全部满足其条件。先递归处理\(u\)的所有儿子,回溯时若u已满足奇偶限制则直接回溯,否则将\((u,f(u))\)选入集合,然后回溯。(这似乎是一种比较常见的树上构造方法)

显然,这样处理完后只有根可能奇偶性不满足

若满足直接输出。

否则,若全局没有\(-1\),则输出\(-1\)

否则,随便找一个\(-1\)的点,记作\(u\),将\(u\)\(root\)路径上的边的选取情况取反(选变不选,不选变选),然后输出即可。

输出可以记录每个点和它父亲的边是输入的哪一条,然后选不选

稍微卡卡常貌似是提交时的最优解,但应该很快就要被刷下去了吧~

Code

这里放上我丑陋的代码

#include<bits/stdc++.h>
#define ri register int
#define ll long long
using namespace std;
const int maxn = 3e5 + 10;
int d[maxn],du[maxn],head[maxn],head1[maxn],ed[maxn],f[maxn],n,m,tot = 1,tot1;
bool chos[maxn],vis[maxn];
struct edge{
	int to,nex;
}e[maxn<<1],e1[maxn<<1];
inline void add(int u,int v){
	e[++tot] = (edge){v,head[u]};
	head[u] = tot;
}
inline int rd(){
	int res = 0,f = 0;
	char ch = getchar();
	for(;ch > '9' || ch < '0';ch = getchar()) if(ch == '-') f = 1;
	for(;ch >= '0' && ch <= '9';ch = getchar()) res = (res<<3) + (res<<1) + ch - 48;
	return f ? -res : res;
}
void dfs(int u){
	vis[u] = 1;
	for(ri i = head[u];i;i = e[i].nex){
		int v = e[i].to;
		if(vis[v]) continue;
		ed[v] = i >> 1;
		f[v] = u;
		e1[++tot1] = (edge){v,head1[u]};
		head1[u] = tot1;
		dfs(v);
	}
}
void solve(int x){
	for(ri i = head1[x];i;i = e1[i].nex) solve(e1[i].to);
	if(du[x] == d[x] || d[x] == -1 || x == 1) return;
	chos[x] = 1;
	du[x] ^= 1; du[f[x]] ^= 1;
	return;
}
void change(int x){//把x到root的边的状态全部取反
	while(x != 1){
		chos[x] ^= 1;
		du[x] ^= 1; du[f[x]] ^= 1;
		x = f[x];
	}
	return;
}
int ans[maxn],cnt = 0;
int cp = -1;
void write(int x){
	if(x > 9) write(x / 10);
	putchar(x % 10 + 48);
}
void prin(int x){
	if(x < 0) putchar('-'),x = -x;
	write(x);
}
int main(){
	n = rd(); m = rd();
	for(ri i = 1;i <= n;++i){
		d[i] = rd();
		if(!(~d[i]) && !(~cp)) cp = i;//cp -> changing positon
	}
	for(ri i = 1,u,v;i <= m;++i)
		u = rd(),v = rd(),add(u,v),add(v,u);
	dfs(1);//建树
	solve(1);//按先保证儿子满足的方式递归处理整棵树
	if(~d[1] && du[1] ^ d[1] && (~cp)) change(cp);//需要change并且可以change
	if(~d[1] && du[1] ^ d[1]){
		puts("-1");
		return 0;
	}
	for(ri i = 2;i <= n;++i)
		if(chos[i]) ans[++cnt] = ed[i];
	prin(cnt); puts("");
	for(ri i = 1;i <= cnt;++i) prin(ans[i]),putchar(' ');
	puts("");
	return 0;
}

完结撒花//QwQ///

posted @ 2021-05-30 22:39  Lumos壹玖贰壹  阅读(27)  评论(0编辑  收藏  举报