1. 程式人生 > 其它 >P5933 [清華集訓2012]串珠子

P5933 [清華集訓2012]串珠子

Lisa

顯然狀態壓縮

然後,對於一個點集S,我們很容易求出這個點集可以形成的任意圖\(F_S\)

這個很容易預處理出來

然後呢,對於這個直接求聯通的方案書並不容易,但是,可以用總方案減去不連通的方案數。

不連通的方案視為兩個點集,一個點集隨便,另一個點集必須聯通。

所以在預處理完了以後,我們首先要做的就是在當前集合裡剝出一個點作為強制的聯通集合的點,然後列舉這個集合剝離後的子集作為隨意點,其子集的補集作為聯通點集。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int n;
int ma[101][101];
int maxn=18;
int f[1<<18];
int dp[1<<18];
int g[1<<18];
int m=1000000007;
int cnt;
signed main(){
	scanf("%d",&n);
	cnt=(1<<n)-1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			scanf("%d",&ma[i][j]);
		}
	}
	for(int k=0;k<=cnt;++k){
		f[k]=1;
		for(int i=1;i<=n;++i){
			if(k&1<<i-1){
				for(int j=i+1;j<=n;++j){
					if(k&1<<j-1){
						f[k]=f[k]*(ma[i][j]+1)%m;
						f[k]%=m;
					}
				}
			}
		}
	}
	for(int k=1;k<=cnt;++k){
		dp[k]=f[k];
		int frm=k^(k&-k);
		for(int i=frm;i;i=(i-1)&frm){
			dp[k]=(dp[k]-f[i]*dp[i^k]%m+m)%m;
		}
	}
	cout<<dp[cnt];
	return 0;
}