#矩阵树定理,高斯消元,容斥定理#洛谷 4336 [SHOI2016]黑暗前的幻想乡

题目


分析

这很明显是矩阵树定理,但是每个建筑公司都恰好修建一条边非常难做,
考虑如果一个建筑公司在某个方案中并没有恰好修建一条边,
那么这种方案一定能在不选其它任意一个公司的方案中被减掉,
那就可以用容斥定理做,枚举选择公司的二进制状态,求出基尔霍夫矩阵


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int mod=1000000007;
int a[18][18],Aa[17][18][18],xo[65536],n,ans,all;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline signed ksm(int x,int y){
	rr int ans=1;
	for (;y;y>>=1,x=1ll*x*x%mod)
	    if (y&1) ans=1ll*ans*x%mod;
	return ans;
}
inline void MO(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
inline void Mo(int &x,int y){x=x<y?x-y+mod:x-y;}
inline signed Gauss(int n){
	rr int ans=1;
    for (rr int i=1,p=1;i<=n;p=++i){
    	for (rr int j=i+1;j<=n;++j)
    	if (a[j][i]>a[p][i]) p=j;
    	if (p!=i){
		    for (rr int j=1;j<=n;++j)
		        swap(a[i][j],a[p][j]);
		    ans=mod-ans;
		}
		if (!a[i][i]) return 0;
		ans=1ll*ans*a[i][i]%mod;
		rr int invt=ksm(a[i][i],mod-2);
    	for (rr int j=1;j<=n;++j)
		if (i!=j){
    		rr int elim=1ll*a[j][i]*invt%mod;
    		for (rr int k=i;k<=n;++k)
    		    Mo(a[j][k],1ll*elim*a[i][k]%mod);
		}
	}
	return ans;
}
signed main(){
	n=iut(),all=1<<(n-1);
	for (rr int i=1;i<n;++i)
	for (rr int CNT=iut();CNT;--CNT){
		rr int x=iut(),y=iut();
		++Aa[i][x][x],Mo(Aa[i][x][y],1);
		++Aa[i][y][y],Mo(Aa[i][y][x],1);
	}
	for (rr int S=1;S<all;++S) xo[S]=xo[S&(S-1)]+1;
	for (rr int S=1;S<all;++S){
		memset(a,0,sizeof(a));
		for (rr int k=0;k<n;++k)
		if ((S>>k)&1){
			for (rr int i=1;i<=n;++i)
			for (rr int j=1;j<=n;++j)
			    MO(a[i][j],Aa[k+1][i][j]);
		}
		rr int t=Gauss(n-1);
		if ((xo[S]^n)&1) MO(ans,t);
		    else Mo(ans,t);
	}
	return !printf("%d",ans);
}
原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13922540.html