【题目】
TC
给定一棵带标号的树,你可以进行至多
K
K
K次操作,每次删除一条边再加上一条边。问能得到多少本质不同的树。答案对
1
0
9
+
7
10^9+7
109+7取模。
n
,
K
≤
50
n,K\leq 50
n,K≤50
【解题思路】
不妨将原树的边看作
1
1
1,不在原树上的边看作
x
x
x,做矩阵树,那么实际上
x
k
x^k
xk项系数就是操作
k
k
k次的答案了。
但是这里矩阵树的每个位置是一个多项式,用
FFT
\text{FFT}
FFT也只能做到
O
(
n
4
log
n
)
O(n^4\log n)
O(n4logn),显然不过了。
不过这里次数界比较小,我们可以直接将
x
=
1
∼
n
x=1\sim n
x=1∼n代入,最后就可以插值回来了。
最后复杂度就是
O
(
n
3
log
n
)
O(n^3\log n)
O(n3logn)
插值一定要从一开始啊。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=55,mod=1e9+7;
int n,fa[N],f[N],ans[N];
namespace Math
{
int fac[N],ifac[N],inv[N],p0[N];
int A[N][N],now[N],sum[N];
int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=upm(x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}
int qpow(int x,int y){int res=1;for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);return res;}
void initmath()
{
inv[0]=inv[1]=1;for(int i=2;i<N;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
fac[0]=1;for(int i=1;i<N;++i)fac[i]=mul(fac[i-1],i);
ifac[N-1]=qpow(fac[N-1],mod-2);for(int i=N-2;~i;--i)ifac[i]=mul(ifac[i+1],i+1);
p0[0]=1;p0[1]=qpow(mod-1,mod-2);for(int i=2;i<N;++i) p0[i]=mul(p0[i-1],p0[1]);
}
int det(int n)
{
int op=1,res=1;
for(int i=1;i<n;++i) for(int j=1;j<n;++j) up(A[i][j],0);
for(int i=1;i<n;++i)
{
int x=i;
for(int j=i;j<n;++j) if(A[j][i]) x=j;
if(x^i) swap(A[x],A[i]),op^=1;
res=mul(res,A[i][i]);
for(int inv=qpow(A[i][i],mod-2),j=1;j<=n;++j) A[i][j]=mul(A[i][j],inv);
for(int j=i+1;j<n;++j)
{
int t=A[j][i];
for(int k=1;k<n;++k) up(A[j][k],-mul(A[i][k],t));
}
}
return upm((op?res:-res));
}
int calc(int x)
{
memset(A,0,sizeof(A));
for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j)
if(fa[i]==j || fa[j]==i) --A[i][j],--A[j][i],++A[i][i],++A[j][j];//tree edge
else A[i][j]-=x,A[j][i]-=x,A[i][i]+=x,A[j][j]+=x;//not tree edge
return det(n);
}
void lagrange(int *a,int n,int *res)
{
sum[0]=1;
for(int i=1;i<=n;++i)
{
for(int j=i;j;--j) sum[j]=mul(sum[j],mod-i),up(sum[j],sum[j-1]);
sum[0]=mul(sum[0],mod-i);
}
for(int i=1;i<=n;++i)
{
int tot=a[i];
tot=mul(tot,ifac[i-1]);tot=mul(tot,mul(p0[n-i],ifac[n-i]));
int coe=mul(p0[1],inv[i]);now[0]=mul(sum[0],coe);
for(int j=1;j<n;++j) now[j]=mul(upm(sum[j]-now[j-1]),coe);
//printf("now:");for(int j=0;j<n;++j)printf("%d ",now[j]); puts("");
for(int j=0;j<n;++j) up(res[j],mul(tot,now[j]));
}
}
/*void lagrange(int *a,int n,int *res)
{
for(int i=1;i<=n;++i)
{
memset(now,0,sizeof(now));now[0]=1;
for(int j=1;j<=n;++j)
{
if(i==j) continue;
int iv=qpow(upm(i+mod-j),mod-2);
for(int k=n;k>=0;--k) now[k]=upm(mul(now[k],mul(mod-j,iv))+(k?mul(now[k-1],iv):0));
}
for(int j=0;j<n;++j) up(res[j],mul(now[j],a[i]));
}
}*/
}
using namespace Math;
class TreeDistance
{
public:
int countTrees(vector<int>p,int K)
{
initmath();n=p.size()+1;
for(int i=2;i<=n;++i) fa[i]=p[i-2]+1;
for(int i=1;i<=n;++i) f[i]=calc(i);
lagrange(f,n,ans);
for(int i=1;i<n;++i) up(ans[i],ans[i-1]);
return ans[min(K,n-1)];
}
};
/*int main()
{
freopen("TC13369.in","r",stdin);
freopen("TC13369.out","w",stdout);
vector<int>a={0, 0, 0, 0, 2, 3, 1, 2, 3, 7, 3, 10, 8, 8, 9, 1, 2, 0, 7, 17, 19, 2, 17, 2, 0,
6, 4, 9, 12, 14, 8, 12, 10, 30, 20, 30, 8, 36, 28, 22, 8, 2, 2, 13, 26, 14, 46, 6, 25};int k=10;
printf("%d\n",(new TreeDistance())->countTrees(a,k));
return 0;
}*/