洛谷 P4336 [SHOI2016]黑暗前的幻想乡

容斥原理套矩阵-树。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <utility>

typedef long long ll;

const int MOD = 1e9 + 7, MAXN = 19;

int qpow(int a, int b){
	int res = 1;
	while(b){
		if(b & 1)
			res = (ll)res * a % MOD;
		a = (ll)a * a % MOD;
		b >>= 1;
	}
	return res;
}

int a[MAXN][MAXN];

int det(int n){
	int res = 1;
	for(int i = 1; i <= n; ++i){
		int r = i;
		for(int j = i; j <= n; ++j)
			if(a[j][i]){
				r = j;
				break;
			}
		if(i ^ r){
			std::swap(a[i], a[r]);
			res = -res;
		}
		if(!a[i][i])
			return 0;
		int inv = qpow(a[i][i], MOD - 2);
		for(int j = i + 1; j <= n; ++j){
			int d = (ll)a[j][i] * inv % MOD;
			for(int k = i; k <= n; ++k)
				a[j][k] = (a[j][k] - (ll)a[i][k] * d % MOD) % MOD;
		}
		res = (ll)res * a[i][i] % MOD;
	}
	return res;
}

int begin[MAXN], end[MAXN];
std::pair<int, int>edge[MAXN * MAXN * MAXN];

int n;

inline int count(unsigned int status){
	int res = 0;
	for(int i = 1; i < n; ++i)
		if(status & (1 << (i - 1)))
			++res;
	return res;
}

inline void insert(int u, int v){
	++a[u][u], ++a[v][v];
	--a[u][v], --a[v][u];
}

int ans;

int main(int argc, char *argv[]){
	std::scanf("%d", &n);
	for(int c, i = 1; i < n; ++i){
		std::scanf("%d", &c);
		begin[i] = end[i - 1], end[i] = begin[i] + c;
		for(int j = begin[i]; j != end[i]; ++j)
			std::scanf("%d%d", &edge[j].first, &edge[j].second); 
	}
	for(int c, s = (1 << (n - 1)) - 1; s; --s){
		c = 0;
		std::memset(a, 0, sizeof a);
		for(int i = 1; i < n; ++i)
			if(s & (1 << (i - 1))){
				++c;
				for(int j = begin[i]; j != end[i]; ++j)
					insert(edge[j].first, edge[j].second);
			}
		ans = ((ll)ans + ((n - c) & 1 ? 1ll : -1ll) * det(n - 1)) % MOD; 
	}
	ans = ((ll)ans + MOD) % MOD;
	std::printf("%d\n", ans);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/natsuka/p/12687020.html