2018-8-10考试 T3. 招募(akekure)

Posted memory-of-winter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018-8-10考试 T3. 招募(akekure)相关的知识,希望对你有一定的参考价值。

题目大意:有$n$个点和$m$条边的图($n - 1 leq m leq n + 5$),每个点要么黑要么白,两个黑点不可以相邻,问方案数

题解:可以发现当图为一棵树的时候只需要一个树形$DP$

$$令f_{i,j}表示在第i个点,它的状态为j(1为黑,0为白)$$
$$f_{i,0}=prodlimits_{j为i的儿子}(f_{j,0}+f_{j,1})(因为它的儿子没有限制,可以黑可以白)$$
$$f_{i,0}=prodlimits_{j为i的儿子}f_{j,0}(它的儿子有限制,必须为白)$$


再来考虑不为树的情况,可以发现这个图最多有$6$条返祖边,我们可以暴力枚举这几条边两端的情况,这样复杂度是$O(4^{m-n+1} imes n)$。

明显是不行的,可以发现两端为黑是一定不可能的,这样复杂度成为了$O(3^{m-n+1} imes n)$。

看来可以的,但是遍历图的常数太大,还是过不了,考虑优化。

发现其实对于一条边,可以变成只处理其中的一个点,这样就只有两种状态了,一种是这个点白点,另一个点就无限制;另一种是这个点为黑点,另一个点就是白点,复杂度就成了$O(2^{m-n+1} imes n)$,轻松$AC$

卡点:1.考试时复杂度为$O(3^{m-n+1} imes n)$

 

C++ Code:

#include <cstdio>
#include <cctype>
#define maxn 100100
using namespace std;
const long long mod = 1000000007;
const long long half = 500000004;
int n, m;
long long ans;
int fa[maxn], oth[50], tot;
int f[maxn][2];

int head[maxn], cnt;
struct Edge {
	int from, to, nxt;
	bool can;
} e[(maxn + 10) << 1];
void add(int a, int b) {
	e[++cnt] = (Edge) {a, b, head[a], true}; head[a] = cnt;
}

char *ch, op[1 << 25 | 100];
inline void read(int &x) {
	x = 0;
	while (isspace(*ch)) ch++;
	while (isdigit(*ch)) x = x * 10 + (*ch++ & 15);
}

bool vis[maxn];
void dfs(int rt) {
	vis[rt] = true;
	int v;
	for (register int i = head[rt]; i; i = e[i].nxt) {
		v = e[i].to;
		if (v != fa[rt]) {
			if (!vis[v]) {
				fa[v] = rt;
				dfs(v);
			} else {
				e[i].can = false;
				if (v > rt) oth[++tot] = i;
			}
		}
	}
}
bool ispoint[maxn], mus[maxn];
inline void real(int rt) {
	int v;
	int &_0 = f[rt][0], &_1 = f[rt][1];
	_0 = _1 = 1;
	if (ispoint[rt]) f[rt][mus[rt] ^ 1] = 0;
	for (int i = head[rt]; i; i = e[i].nxt) {
		if (e[i].can) {
			v = e[i].to;
			if (v != fa[rt]) {
				real(v);
				_0 = (1ll * _0 * (f[v][1] + f[v][0])) % mod;
				_1 = (1ll * _1 * f[v][0]) % mod;
			}
		}
	}
}
void run(int x) {
	if (x > tot) {
		real(1);
		ans = (ans + f[1][1] + f[1][0]) % mod;
		return ;
	}
	int u = e[oth[x]].from, v = e[oth[x]].to;
	bool &iu = ispoint[u], &iv = ispoint[v], &mu = mus[u], &mv = mus[v];
	if (iu && iv) {
		run(x + 1);
		return ;
	}
	if (iu || iv) {
		if (iv) u ^= v ^= u ^= v;
		if (mus[u]) {
			ispoint[v] = true;
			mus[v] = false;
			run(x + 1);
			ispoint[v] = false;
		} else {
			run(x + 1);
		}
		return ;
	}
	iu = true;
	mu = false;
	run(x + 1);
	iu = false;
	iu = iv = true;
	mu = true;
	mv = false;
	run(x + 1);
	iv = iu = false;
}
int main() {
	fread(ch = op, 1, 1 << 25, stdin);
	read(n), read(m);
	for (register int i = 0; i < m; i++) {
		int a, b;
		read(a), read(b);
		add(a, b);
		add(b, a);
	}
	dfs(1);
	run(1);
	printf("%lld
", ans);
	return 0;
}

  

以上是关于2018-8-10考试 T3. 招募(akekure)的主要内容,如果未能解决你的问题,请参考以下文章

某考试 T3 C

考试题 T3

2019.10.03考试报告

[考试反思]1004csp-s模拟测试59:惊醒

2.28考试

2.21考试小记