题面传送门
这个\(n\)这么小肯定是拿来状压的。
我们考虑设\(F_{i}\)为仅用\(i\)种公司生成的树方案数。
但是这个东西不是很好做,我们考虑设一个弱一点的状态:\(G_i\)表示用了\(i\)个公司的方案数。
这个\(G\)显然可以状压在\(O(n^32^n)\)时间内达到。
然后考虑\(G\)怎么推出\(F\)
假设我们\(F_{1…i-1}\)都做好了,然后要推\(F_i\)
这个考虑容斥,\(F_i\)总共有\(C^{n}_{i}\)种,然后对于一个\(j\)总共有\(C_{j}^{i}\)种,所以总共是两个乘起来。
然后对于\(j\)有\(C_{j}^{n}\)种可能,因为是均匀分布的就是除一下即可。
code:
#include<bits/stdc++.h> #define I inline #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define abs(x) ((x)>0?(x):-(x)) #define re register #define ll long long #define db double #define N 17 #define M 20000 #define mod 1000000007 #define eps (1e-7) #define U unsigned int #define it iterator #define Gc() getchar() #define Me(x,y) memset(x,y,sizeof(x)) using namespace std; int n,m[N+5],tot,now,x[N+5][N*N+5],y[N+5][N*N+5];ll A[N+5][N+5],ans,frc[N+5],inv[N+5],W[N+5]; I void insert(int x,int y){A[x][x]++;A[y][y]++;A[x][y]--;A[y][x]--;} I void swap(ll &x,ll &y){x^=y^=x^=y;} I ll calc(){ re int i,j,h;ll ans=1;for(i=2;i<=n;i++){ for(j=i+1;j<=n;j++){ while(A[j][i]){now=mod-A[i][i]/A[j][i];for(h=i;h<=n;h++)A[i][h]=(A[i][h]+A[j][h]*now)%mod,swap(A[i][h],A[j][h]); ans*=-1;} } }for(i=2;i<=n;i++) ans=ans*A[i][i]%mod;return (ans+mod)%mod; } I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),y>>=1,x=x*x%mod;return ans;} int main(){ freopen("1.in","r",stdin); re int i,j,h;scanf("%d",&n);for(i=1;i<n;i++) for(scanf("%d",&m[i]),j=1;j<=m[i];j++)scanf("%d%d",&x[i][j],&y[i][j]); for(frc[0]=i=1;i<=n;i++) frc[i]=frc[i-1]*i%mod;inv[n]=mpow(frc[n]);for(i=n-1;~i;i--) inv[i]=inv[i+1]*(i+1)%mod; for(i=1;i<(1<<n-1);i++){ Me(A,0);for(j=1;j<n;j++){ if(!(i&(1<<j-1))) continue;for(h=1;h<=m[j];h++) insert(x[j][h],y[j][h]); } now=i;tot=0;while(now) tot++,now-=now&-now;W[tot]+=calc(); }for(i=1;i<n;i++)for(j=i-1;j;j--) W[i]=(W[i]-W[j]*frc[n-j-1]%mod*inv[n-i-1]%mod*inv[i-j]%mod+mod)%mod;printf("%lld\n",W[n-1]); }