矩陣樹定理+容斥--luoguP4336 [SHOI2016]黑暗前的幻想鄉
阿新 • • 發佈:2018-12-02
把所有公司的合法邊都連起來,構成一個無向圖
合法的樹需要滿足兩個條件:
是圖的生成樹
每個邊分給不同的公司
直接算會算重,考慮容斥
可以用總數-一個公司沒有+兩個公司沒有-三個…
每一次都要做一遍高斯消元,複雜度是
看起來十分不可過,但就是過了
關於矩陣樹定理可以看這裡
程式碼如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 20
#define LL long long
using namespace std;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
const int maxn=(1<<17)+5;
const int mod=1e9+7;
int n,a[N][N],ed,ans;
vector< pair<int,int> > vec[N];
inline LL qpow(LL x,int k){
LL ret=1;
while(k){
if(k&1) (ret*=x)%=mod;
(x*=x)%=mod; k>>=1;
} return ret%mod;
}
inline int Matrix_tree(){
for(int i=1;i<n;i++)
for(int j=1;j<n;j++)
a[i][j]=(a[i][j]+mod)%mod;
int ans=1,f=1;
for(int i=1;i<n;i++){
for(int j=i+1;j<n;j++){
if(!a[j][i]) continue;
int A=a[i][i],B=a[j][i];
while(B){
int t=A/B;
A%=B; swap(A,B);
for(int k=i;k<n;k++)
a[i][k]=(a[i][k]-1LL*t*a[j][k]%mod+mod)%mod;
for(int k=i;k<n;k++) swap(a[i][k],a[j][k]);
f=-f;
}
}
if(!a[i][i]) return 0;
ans=1LL*ans*a[i][i]%mod;
}
if(f==-1) ans=mod-ans;
return ans%mod;
}
int main(){
n=rd(); ed=1<<(n-1);
for(int i=1;i<n;i++){
int m=rd();
for(int j=1;j<=m;j++){int x=rd(),y=rd(); vec[i].push_back(make_pair(x,y));}
}
for(int i=0;i<ed;i++){
memset(a,0,sizeof a); int cnt=0;
for(int j=1;j<n;j++)
if((1<<(j-1))&i){
++cnt;
for(int k=0;k<vec[j].size();k++){
int u=vec[j][k].first,v=vec[j][k].second;
a[u][u]++,a[u][v]--; a[v][v]++,a[v][u]--;
}
}
if((n-1-cnt)&1) ans-=Matrix_tree();
else ans+=Matrix_tree();
ans=(ans%mod+mod)%mod;
}
printf("%d\n",ans);
return 0;
}