题目出自吉大附中 jkxing
题目大意
k ≤ n ≤ 50 ~~~~~~k \leq n \leq 50 k≤n≤50,方案数 % 998244353
题解
~~~~~~ 这个题很妙。。。
~~~~~~
我们知道矩阵树定理,即基尔霍夫矩阵的任意一个
n
−
1
n-1
n−1 阶主子式就是生成树个数。
~~~~~~
现在加了个限制,即原图的边最多不选
k
k
k 个,怎么做呢?
~~~~~~
其实就是要把原图边和非原图边用不同的方法表示。
~~~~~~
比如用
x
x
x 表示一条原图边,用
1
1
1 表示非原图边。这里的
x
x
x 是多项式的一次未知数的意思。
~~~~~~
比如有个点连了
3
3
3 条原图边、
2
2
2 条非原图边,那么它的度数就是
3
x
+
2
3x+2
3x+2。相应地,邻接矩阵里也是用
x
x
x 和
1
1
1 来表示。
~~~~~~
那么此时的主子式就是一个多项式,其中
x
i
x^i
xi 的系数就表示保留
i
i
i 条原图边的方案数。
~~~~~~ 现在的问题是如何把这个多项式求出来。可以用插值法的思想,给 x x x 代入 n n n 个不同的值,最后再插值求多项式。可以用拉格朗日插值法什么的。我用的是 DFT。
代码
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int maxn=55, maxlen=150;
const LL mo=998244353;
int n,K,len;
bool mp[maxn][maxn];
LL G0[maxn][maxn][2];
LL mi(LL x,LL y)
{
LL re=1;
for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
return re;
}
LL G[maxn][maxn],w[maxlen];
LL Determinant(int n)
{
LL d=1;
fo(i,0,n)
{
fo(j,i,n) if (G[j][i]>0)
{
swap(G[i],G[j]);
if (j!=i) d*=-1;
break;
}
fo(j,i+1,n)
{
LL c=(-G[j][i]*mi(G[i][i],mo-2)%mo+mo)%mo;
fo(k,0,n) G[j][k]=(G[j][k]+c*G[i][k])%mo;
}
}
fo(i,0,n) d=d*G[i][i]%mo;
return (d%mo+mo)%mo;
}
LL y[maxlen];
int main()
{
scanf("%d %d",&n,&K);
K=n-1-K;
if (n==1) {printf("1\n"); return 0;}
for(len=1; len<n; len<<=1);
fo(i,1,n-1)
{
int x;
scanf("%d",&x);
G0[x][x][1]++, G0[i][i][1]++;
G0[x][i][1]--, G0[i][x][1]--;
mp[x][i]=mp[i][x]=1;
}
fo(i,0,n-2)
fo(j,i+1,n-1) if (!mp[i][j])
{
G0[i][i][0]++, G0[j][j][0]++;
G0[i][j][0]--, G0[j][i][0]--;
}
w[0]=1;
w[1]=mi(3,(mo-1)/len);
fo(i,2,len) w[i]=(w[i-1]*w[1])%mo;
fo(wi,0,len-1)
{
fo(i,0,n-1)
fo(j,0,n-1) G[i][j]=((G0[i][j][0]+G0[i][j][1]*w[wi])%mo+mo)%mo;
y[wi]=Determinant(n-2);
}
LL ans=0;
fo(i,K,len-1)
fo(j,0,len-1) ans=(ans+y[j]*mi(w[len-i],j))%mo;
printf("%lld\n",ans*mi(len,mo-2)%mo);
}