【矩阵树定理+拉格朗日插值】TC13369 [TCO14 Round 3B] TreeDistance

【题目】
TC
给定一棵带标号的树,你可以进行至多 K K K次操作,每次删除一条边再加上一条边。问能得到多少本质不同的树。答案对 1 0 9 + 7 10^9+7 109+7取模。
n , K ≤ 50 n,K\leq 50 n,K50

【解题思路】
不妨将原树的边看作 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=1n代入,最后就可以插值回来了。
最后复杂度就是 O ( n 3 log ⁡ n ) O(n^3\log n) O(n3logn)

插值一定要从一开始啊。

【参考代码】

#include
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
			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
}
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);

	vectora={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;
}*/

你可能感兴趣的:(数论-拉格朗日插值法,其他-矩阵树定理)