待补 重要思考:求给无向图定向使得其变为DAG的方案数

今天比赛考到了,不会,丢了 100 分。

rk2,380 -> rk15,280

别问为什么 T4 没过,因为不会 T2。

方法一 \(O(3^n)\)

\(f_S\) 为子集 \(S\) 内定向得到 DAG 的方案。

\(f_S = \sum\limits_{\emptyset \not= T\subset S, \text{T 为独立集}} (-1)^{|T| - 1}f_{S - T}\)

考虑 DAG 的分解构造过程,可以将其分为出/入度等于 \(0\) 的最大点集,删除/剥离一层继续这样,考虑逆向构造 DAG,但是发现存在重复计数,可以采用容斥,对于非空点集 \(T\),容斥系数为 \((-1)^{|T| - 1}\)

关于容斥系数:有机会总结一下。关于本题,设 \(T\) 的计算容斥系数为 \(g(T)\),而真实容斥系数为 \(1\),根据计算方式,大的会被每个小的计算一次(有些时候是有关组合数的系数,但这里根据定义和公式是 \(1\) 次),于是有 \(\forall S, \sum\limits_{T\subset S}g(T) = 1\)

可以构造 \(g(T) = (-1)^{|T| - 1}\)

枚举子集复杂度 \(O(3^n)\)。考虑是不是可以 \(O(3^n)\) 求得容斥系数?

code
const int N = 20;
const int mod = 1e9 + 7;

int n, m;
int to[N];
int f[1 << N], g[1 << N];

inline void qadd(int &a, int b) {
	a += b;
	if (a >= mod) a -= mod;
}

void calc() {
	read(n, m);
	rep (i, 1, m) {
		int a, b; read(a, b);
		--a; --b;
		to[a] |= 1 << b;
		to[b] |= 1 << a;
	}
	g[0] = 1;
	rep (s, 1, (1 << n) - 1) {
		rep (j, 0, n - 1) {
			if (s >> j & 1) {
				g[s] = g[s ^ (1 << j)] & ((s & to[j]) == 0);
				break;
			}
		}
	}

	f[0] = 1;
	rep (s, 1, (1 << n) - 1) {
		for (int t = s; t; t = (t - 1) & s) {
			if (g[t] && f[s ^ t]) {
				if (__builtin_popcount(t) & 1) qadd(f[s], f[s ^ t]);
				else qadd(f[s], mod - f[s ^ t]);
			}
		}
	}
	write(f[(1 << n) - 1]);
}

方法二 \(O(n^22^n)\)

貌似子集卷积,记得补

1 LUOGU

2 NFLS

posted @ 2024-09-01 20:34  SkyMaths  阅读(5)  评论(0编辑  收藏  举报