zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    AC_9. 分组背包问题
    AC_8. 二维费用的背包问题
    AC_7混合背包问题
    AC_5. 多重背包问题 II
    AC_4. 多重背包问题 I
    AC_3. 完全背包问题
    小郡肝火锅点餐系统——测试部署发布
    Vue脚手架搭建
    归并排序-总结
    小郡肝火锅点餐系统——项目文档
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9457065.html
Copyright © 2011-2022 走看看