Description
铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。
Solution
设
f
S
f_S
fS表示点集为S时的方案数,容斥一下可以得到
f
S
=
g
S
−
∑
T
⊂
S
f
T
⋅
g
S
\
T
f_S=g_S-\sum\limits_{T\subset S}{f_T\cdot g_{S\backslash T}}
fS=gS−T⊂S∑fT⋅gS\T,其中
g
S
g_S
gS表示集合为S的导出子图,所有边权+1的乘积
直接做是
O
(
3
n
)
O(3^n)
O(3n)的,为了不重复我们要保证
S
S
S和
T
T
T的最低非0位相等
考虑一个比较牛逼的做法,观察到这本质上是一个子集卷积,难点在于解决最低非0位相等的情况
注意到我们钦定任意一位相等都是可以的,观察每一个子集的贡献可以发现
c
S
⋅
f
S
=
g
S
−
∑
T
⊂
S
f
T
⋅
c
T
⋅
g
S
\
T
c_S\cdot f_S=g_S-\sum\limits_{T\subset S}{f_{T}\cdot c_T}\cdot g_{S\backslash T}
cS⋅fS=gS−T⊂S∑fT⋅cT⋅gS\T
其中
c
T
c_T
cT表示集合T中1的数量
那么我们就可以做
O
(
n
2
⋅
2
n
)
O(n^2\cdot 2^n)
O(n2⋅2n)的子集卷积,把
f
S
⋅
c
S
f_S\cdot c_S
fS⋅cS带进去做就可以了
写出来被卡常了qaq
Code
#include <stdio.h>
#include <string.h>
#include <math.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
#define lowbit(x) (x&-x)
typedef long long LL;
const int MOD=1000000007;
const int ny2=(MOD+1)/2;
int f[21][1049595],g[21][1049595];
int p[1049595],fac[55];
int c[1049595];
void upd(int &x,int v) {
x+=v; (x>=MOD)?(x-=MOD):0;
}
LL ksm(int x,int dep) {
int ret=1; x=(x%MOD+MOD)%MOD;
for (;dep;dep>>=1) {
(dep&1)?(ret=1LL*ret*x%MOD):0;
x=1LL*x*x%MOD;
}
return ret;
}
void FWT(int *a,int n,int f) {
for (register int i=1;i<n;i<<=1) {
for (register int j=0;j<n;j+=(i<<1)) {
for (register int k=0;k<i;++k) {
if (f==1) upd(a[j+k+i],a[j+k]);
else upd(a[j+k+i],MOD-a[j+k]);
}
}
}
}
int main(void) {
freopen("union.in","r",stdin);
freopen("union.out","w",stdout);
int n; scanf("%d",&n);
int lim=(1<<n)-1;
rep(i,0,lim) p[i]=1,c[i]=c[i>>1]+(i&1);
rep(i,1,n) rep(j,0,lim) f[i][j]=1;
rep(i,1,n) rep(j,i+1,n) {
int w,x=i-1,y=j-1; scanf("%d",&w);
f[x+1][1<<y]=1LL*f[x+1][1<<y]*(w+1)%MOD;
f[y+1][1<<x]=1LL*f[y+1][1<<x]*(w+1)%MOD;
}
rep(i,1,n) rep(j,1,lim) {
int x=lowbit(j);
f[i][j]=1LL*f[i][j^x]*f[i][x]%MOD;
}
p[0]=1;
rep(i,1,lim) {
int x=lowbit(i);
p[i]=1LL*p[i^x]*f[1+(int)log2(x)][i^x]%MOD;
g[c[i]][i]=p[i];
}
fill(f,0);
rep(i,1,lim) if (c[i]==1) f[1][i]=g[1][i]=1;
FWT(f[1],lim+1,1);
rep(i,1,n) FWT(g[i],lim+1,1);
rep(i,2,n) {
rep(j,1,i-1) rep(k,0,lim) upd(f[i][k],1LL*f[j][k]*g[i-j][k]%MOD);
FWT(f[i],lim+1,-1);
rep(k,0,lim) {
if (c[k]!=i) {
f[i][k]=0;
continue;
}
f[i][k]=1LL*p[k]*c[k]%MOD-f[i][k];
(f[i][k]<0)?(f[i][k]+=MOD):0;
}
if (i!=n) FWT(f[i],lim+1,1);
}
printf("%lld\n", 1LL*f[n][lim]*ksm(n,MOD-2)%MOD);
return 0;
}