题目
题意:
对于一个n个点的图,每对点
u
,
v
u,v
u,v间有
p
0
p_0
p0的几率没有边,
p
1
p_1
p1的几率有权值为1的边,
p
2
p_2
p2…边权
<
=
4
<=4
<=4,对于
n
−
1
<
=
t
<
=
(
n
−
1
)
(
m
a
x
v
a
l
o
f
e
d
g
e
)
n-1<=t<=(n-1)(maxval \ of \ edge)
n−1<=t<=(n−1)(maxval of edge)的每个
t
t
t求有最小生成树且最小生成树大小为
t
t
t的几率
(
m
o
d
1000000007
)
\pmod {1000000007}
(mod1000000007)
考虑克鲁斯卡尔求生成树的过程,那么我们可以从小到大加入边,在
d
p
dp
dp上即为
f
i
,
j
f_{i,j}
fi,j表示
<
=
i
<=i
<=i的边联通特定的
j
j
j个点的概率。
发现不好转移,但是还是可以转移的。
枚举特定的点中标号最小的点所在的由
<
=
i
−
1
<=i-1
<=i−1的边联通的极大联通块大小,这个联通块往外连的边中考虑边权为
i
i
i的边,它连向了一个由
<
=
i
<=i
<=i的边联通的且不包含当前联通块的极大联通块,这个联通块是比标号最小的点所在的由
<
=
i
<=i
<=i的边联通的极大联通块小的,是一个子问题,那么它的方案数是可以在之前就计算出来的,相当于一个点有很多个儿子,我们从小到大枚举儿子子树大小然后枚举这个大小的有多少个再组合数分配标号去重,这样就可以转移了。
但是我们这样只算了联通的概率,并没有计算最小生成树的值。。。。。。
那么就再来一维?
我们用生成函数表示最后一维,
x
t
x^t
xt前的系数表示权值为
t
t
t的最小生成树的方案。
因为我们可以发现我们之前这个究极复杂的DP是可以算最小生成树的值的。
然后用
x
=
0...
(
n
−
1
)
(
m
a
x
v
a
l
o
f
e
d
g
e
−
1
)
x=0...(n-1)(maxval \ of \ edge - 1)
x=0...(n−1)(maxval of edge−1)带入计算答案然后插值得到多项式即可。
原题解(有几处(官方)错误):
记
f
i
,
j
f_{i,j}
fi,j 表示 i 个点的图由权值$ ≤ t $的边连通的概率,初值
f
0
,
1
=
1
;
f
0
,
j
=
0
(
2
≤
j
≤
n
)
f_{0,1} = 1; f_{0,j} = 0 (2 ≤ j ≤ n)
f0,1=1;f0,j=0(2≤j≤n)。
假设
f
i
,
j
(
0
≤
i
≤
t
−
1
;
1
≤
j
≤
n
)
f_{i,j} (0 ≤ i ≤ t − 1; 1 ≤ j ≤ n)
fi,j(0≤i≤t−1;1≤j≤n)均已求出,当前考虑到权值为 t 的边。考虑一个 dfs 由 ≤ t 的边
构成的连通块的过程:从 1 号点所在的由
≤
t
−
1
≤ t − 1
≤t−1的边构成的连通块出发,按照从小到大的顺序递归访
问其他由 ≤ t 的边构成的连通块(大小相同则优先访问块内最小编号较小的)。现在 dp 这个 dfs 过程,
记
g
t
,
i
,
j
g_{t,i,j}
gt,i,j表示从 1 号点所在的由
≤
t
−
1
≤ t − 1
≤t−1 的边构成的大小为 i 的连通块出发,已经访问过的其他由 ≤ t 的
边构成的连通块大小之和是 j 的概率,按照由
≤
t
≤ t
≤t 的边构成的连通块的大小 s 从小到大考虑,先计算出
f
t
,
s
f_{t,s}
ft,s,再枚举
g
t
,
i
,
j
g_{t,i,j}
gt,i,j 挂上多少个大小为 s 的连通块,需要和挂上的连通块之间连
≥
t
≥ t
≥t 的边或者不连边,但
是至少有一条权值为 t 的边,并且挂上的连通块之间两两不能连权值
≤
t
≤ t
≤t 的边。不难分析这样 dp 一次
的复杂度是
O
(
k
n
3
l
o
g
n
)
O(kn^3 log n)
O(kn3logn)。
至此还没有考虑最小生成树,为了避免在状态里额外记录权值和,每挂上一个大小为 s 的连通块就
给概率额外乘上
x
s
−
1
x_{s−1}
xs−1,最后会得到一个关于 x 的多项式,其中
x
t
x^t
xt 项的系数就是最小生成树是
t
+
(
n
−
1
)
t+ (n−1)
t+(n−1)
的概率,分别取
x
=
0
;
1
;
2
;
3
,
(
k
−
1
)
(
n
−
1
)
x = 0; 1; 2; 3,(k − 1)(n − 1)
x=0;1;2;3,(k−1)(n−1) 代入求值,最后使用插值或者高斯消元求出多项式即可。
另外标程对于
x
i
=
i
,
0
<
=
i
<
=
n
x_i=i,0<=i<=n
xi=i,0<=i<=n的拉格朗日插值求多项式的方法十分巧妙(我精简后的代码只有7行)。
对于把
0
0
0也带入的连续整数的拉格朗日插值。
对于
y
j
y_j
yj后面乘上的式子的分母不是
1
1
1开始的那种
(
−
1
)
n
−
j
(
j
−
1
)
!
(
n
−
j
)
!
(-1)^{n-j}(j-1)!(n-j)!
(−1)n−j(j−1)!(n−j)!,而是
(
−
1
)
n
−
j
j
!
(
n
−
j
)
!
(-1)^{n-j}j!(n-j)!
(−1)n−jj!(n−j)!
这样我们就可以用巧妙的累和而不是直接计算求出系数。
for(int
j=n;j>=i;j--)
a[j]=1ll*(a[j]-a[j-1])*inv[i]
\texttt{ for(int j=n;j>=i;j--) a[j]=1ll*(a[j]-a[j-1])*inv[i]}
for(int j=n;j>=i;j--) a[j]=1ll*(a[j]-a[j-1])*inv[i]
这一行中,每一个
a
j
a_j
aj会对
a
i
a_i
ai贡献
(
i
j
)
÷
i
!
∗
a
[
j
]
\binom{i}{j} \div {i!} * a[j]
(ji)÷i!∗a[j],恰好就是
a
[
j
]
j
!
(
i
−
j
)
!
\frac {a[j]}{j!(i-j) !}
j!(i−j)!a[j]
s[j]
=
((j?s[j-1]:0)
-
(i-1ll)
*
s[j])
\texttt{s[j] = ((j?s[j-1]:0) - (i-1ll) * s[j])}
s[j] = ((j?s[j-1]:0) - (i-1ll) * s[j])
这一行中,
s
[
j
]
s[j]
s[j]存的是前
i
−
2
i-2
i−2个数(包括
0
,
1
,
2...
i
−
1
0,1,2...i-1
0,1,2...i−1)选择
j
j
j个他们的乘积和。
然后再加乘起来就刚好是答案,太巧也太强了。
#include<bits/stdc++.h>
#define maxn 41
#define mod 1000000007
using namespace std;
int n,k;
int f[5][maxn],sp[6],p[5],g[maxn][maxn],tr[maxn][maxn],tb[maxn][maxn];
int Pow(int base,int k){
int ret = 1;
for(;k;k>>=1,base=1ll*base*base%mod)
if(k&1)
ret=1ll*ret*base%mod;
return ret;
}
int inv[maxn*maxn]={1,1},fac[maxn*maxn]={1,1},invf[maxn*maxn]={1,1},c[maxn*maxn][maxn*maxn],pw[maxn*maxn];
vector<int>interpolation(vector<int>a,int n){// only when x_i = i , 0 <= i <= n
vector<int>s(n+1),r(n+1);
r[0]=a[0],s[0]=1;
for(int i=1;i<=n;i++){
for(int j=n;j>=i;j--) a[j]=1ll*(a[j]-a[j-1])*inv[i]%mod;
for(int j=i;j>=0;j--)
s[j] = ((j?s[j-1]:0) - (i-1ll) * s[j]) % mod,
r[j] = (r[j] + s[j] * 1ll * a[i]) % mod;
}
return r;
}
int main(){
int T;
c[0][0] = 1;
for(int i=1;i<maxn*maxn;i++){
c[i][0] = 1;
for(int j=1;j<=i;j++)
c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
}
for(int i=2;i<maxn*maxn;i++)
fac[i] = 1ll * fac[i-1] * i % mod,
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
invf[i] = 1ll * invf[i-1] * inv[i] %mod;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&k);
for(int i=0;i<=k;i++) scanf("%d",&p[i]) , p[i] = p[i] * 1ll * Pow(100,mod-2) % mod;
sp[k+1] = p[0];
for(int i=k;i>=0;i--) sp[i] = (sp[i+1] + p[i]) % mod;
vector<int>ar;
for(int x=0;x<=(k-1)*(n-1);x++){
memset(f,0,sizeof f);
f[0][1] = 1;
pw[0] = 1;
for(int i=1;i<=n;i++) pw[i] = pw[i-1] * 1ll * x % mod;
for(int t=1;t<=k;t++){
memset(g,0,sizeof g);
for(int i=0;i<=n;i++)
for(int j=0;j+i<=n;j++){
tb[i][j] = Pow(sp[t+1],i*j);
tr[i][j] = (Pow(sp[t],i*j)-tb[i][j]) % mod;
}
for(int i=1;i<=n;i++)
g[i][0] = 1;
for(int s=1;s<=n;s++){
for(int i=1;i<=s;i++) f[t][s] = (f[t][s] + 1ll * g[i][s-i] * c[s-1][i-1] % mod * f[t-1][i]) % mod;
for(int i=1;i<=n;i++)
for(int j=n-i;j>=0;j--)
for(int k=1,sum=1,cr=1ll*tr[i][s]*f[t][s]%mod*pw[t-1]%mod;k*s+j<=n-i;k++){
sum = 1ll * sum * cr % mod * tb[(k-1)*s+j][s] % mod * c[k*s+j][s] % mod;
g[i][k*s+j] = (g[i][k*s+j] + 1ll * sum * g[i][j] % mod * invf[k]) % mod;
}
}
}
ar.push_back(f[k][n]);
}
ar = interpolation(ar,(k-1)*(n-1));
for(int i=0;i<=(k-1)*(n-1);i++)
printf("%d%c",(ar[i]+mod)%mod," \n"[i==(k-1)*(n-1)]);
}
}