LOJ#2027. 「SHOI2016」黑暗前的幻想乡+ #2091. 「ZJOI2016」小星星((FWT or 容斥)+ 其它算法)

https://loj.ac/problem/2027
https://loj.ac/submission/831930

https://loj.ac/problem/2091
https://loj.ac/submission/831907

对于T1,发现是要每个颜色恰好选一个。

对于T2,发现是要每个点恰好对应图中的一个点。

所以可以直接容斥,枚举(S)表示只能选(S)中的点,求个方案数,容斥系数是((-1)^{全集大小-|S|})

或者说用FWT理解,在做XXX的过程带个二进制状态表示已选那些,每次做or卷积,这样做(比如T1矩阵树里面怎么搞)当然是不行的。

但可以在外面在FWT,变成点积就是普通的数了,最后再IFWT回去,你发现和容斥的式子是一样的。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;

const int mo = 1e9 + 7;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 20;

int n;
int m[N], a[N][N * N][2];

ll b[N][N];

ll solve(ll (*a)[N], int n) {
	ll ans = 1;
	fo(i, 1, n) {
		int u = -1;
		fo(j, i, n) if(a[j][i]) { u = j; break;}
		if(u == -1) return 0;
		if(u > i) {
			ans = -ans;
			fo(j, i, n) swap(a[i][j], a[u][j]);
		}
		ll v = a[i][i];
		ans = ans * v % mo;
		v = ksm(v, mo - 2);
		fo(j, i, n) a[i][j] = a[i][j] * v % mo;
		fo(j, i + 1, n) if(a[j][i]) {
			v = -a[j][i];
			fo(k, i, n) a[j][k] = (a[j][k] + a[i][k] * v) % mo;
		}
	}
	return ans;
}

int main() {
	freopen("a.in", "r", stdin);
	scanf("%d", &n);
	fo(i, 1, n - 1) {
		scanf("%d", &m[i]);
		fo(j, 1, m[i]) scanf("%d %d", &a[i][j][0], &a[i][j][1]);
	}
	ll ans = 0;
	for(int s = 1; s < (1 << (n - 1)); s ++) {
		fo(i, 1, n) fo(j, 1, n) b[i][j] = 0;
		int xs = ((n - 1) % 2 ? -1 : 1);
		fo(i, 1, n - 1) if(s >> (i - 1) & 1) {
			xs = -xs;
			fo(j, 1, m[i]) {
				int x = a[i][j][0], y = a[i][j][1];
				b[x][x] ++; b[y][y] ++; b[x][y] --; b[y][x] --;
			}
		}
		ans = (ans + solve(b, n - 1) * xs) % mo;
	}
	ans = (ans % mo + mo) % mo;
	pp("%lld
", ans);
}
#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("
")
using namespace std;

const int N = 20;

int n, m, x, y;
int bz[N][N], bx[N][N];

int fa[N];

int s;

int d[N], d0;

#define ull unsigned long long

ull f[N][N];

void dg(int x) {
	fo(i ,1, d0) f[x][i] = 1;
	fo(y, 1, n) if(bz[x][y] && fa[x] != y) {
		fa[y] = x;
		dg(y);
		fo(i, 1, d0) {
			ull s = 1;
			fo(j, 1, d0) if(bx[d[i]][d[j]])
				s += f[y][j];
			f[x][i] = f[x][i] * s;
		}
	}
}

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		bx[x][y] = bx[y][x] = 1;
	}
	fo(i, 1, n - 1) {
		scanf("%d %d", &x, &y);
		bz[x][y] = bz[y][x] = 1;
	}
	ull ans = 0;
	for(int s = 1; s < (1 << n); s ++) {
		d0 = 0;
		ff(j, 0, n) if(s >> j & 1)
			d[++ d0] = j + 1;
		int xs = ((n - d0) % 2 ? -1 : 1);
		dg(1);
		ll sum = 0;
		fo(i, 1, d0) sum += f[1][i];
		if(xs == 1) ans += sum; else ans -= sum;
	}
	pp("%llu
", ans);
}
原文地址:https://www.cnblogs.com/coldchair/p/13089043.html