题目
Luogu
个点,修成一棵树,
个公司,每个公司能修一些边,但只能修一条,求方案数
思路
由于是树,所以条件转化(最重要的一步):
[每个公司修一条边]=[每个公司修边]=[
修
修
…
修]
于是
枚举公司然后矩阵树即可
时间复杂度
另解
自己瞎想的
矩阵树每个点看成一个
项的多项式,每一项为
开始时形如
或者
之类的
好像不能消元…咕了
代码
//#pragma GCC optimize(2)
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<cstdio>
#include<queue>
#include<cmath>
#include<vector>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define ULL unsigned long long
int read(){
int f=1,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
return f*x;
}
#define MAXN 300
#define INF 0x3f3f3f3f
#define Mod (int)(1e9+7)
LL mat[MAXN+5][MAXN+5],a[MAXN+5][MAXN+5];
LL Pow(LL x,int y){
LL ret=1;
while(y){
if(y&1) ret=ret*x%Mod;
x=x*x%Mod,y>>=1;
}
return ret;
}
int Det(int n){
n--;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
mat[i][j]=(a[i][j]+Mod)%Mod;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++)
if(!mat[i][i]&&mat[j][i]){
swap(mat[i],mat[j]);
break;
}
LL Inv=Pow(mat[i][i],Mod-2);
for(int j=i+1;j<=n;j++){
LL tmp=mat[j][i]*Inv%Mod;
for(int k=i;k<=n;k++)
mat[j][k]=(mat[j][k]-tmp*mat[i][k]%Mod+Mod)%Mod;
}
}
LL ret=1;
for(int i=1;i<=n;i++)
ret=ret*mat[i][i]%Mod;
return ret;
}
int n;
LL ans;
vector<pair<int,int> > Edge[MAXN+5];
void DFS(int d,int k){
if(d==n){
ans=(ans+Mod+k*Det(n))%Mod;
return ;
}
DFS(d+1,-k);
for(int i=0;i<(int)Edge[d].size();i++){
int u=Edge[d][i].first,v=Edge[d][i].second;
a[u][u]++,a[v][v]++,a[u][v]--,a[v][u]--;
}
DFS(d+1,k);
for(int i=0;i<(int)Edge[d].size();i++){
int u=Edge[d][i].first,v=Edge[d][i].second;
a[u][u]--,a[v][v]--,a[u][v]++,a[v][u]++;
}
return ;
}
int main(){
n=read();
for(int i=1;i<n;i++){
int m=read();
for(int j=1;j<=m;j++)
Edge[i].push_back(make_pair(read(),read()));
}
DFS(1,1);
printf("%lld\n",ans);
return 0;
}