某考试 T2 Tree

2 树

2.1 题目描述

给一棵n 个节点的树,节点分别编号为0 到n - 1。
你可以通过如下的操作来修改这棵树:首先先删去树上的一条边,此时树
会分裂为两个连通块,然后在两个连通块之间加上一条新的边使得它们变成一
棵新的树。
问有多少棵n 个节点的树可以通过对原树进行不超过k 次这样的操作来
得到,答案对109 + 7 取模。如果有一条边(u; v) 出现在了树A 中且不在树B
中,我们就认为树A 和树B 是不同的。
2.2 输入格式
第一行为两个整数n; k。
接下来一行用n - 1 个整数a1; a2; a3; :::; an-1 描述给定的树。其中ai 表
示节点i 和节点ai 之间有一条边,保证ai < i。
2.3 输出格式
输出一个整数表示答案对109 + 7 取模后的值。

2.4 输入样例一
3 1
0 0


2.5 输出样例一
3

 


2.6 输入样例二
6 4
0 1 1 3 3


2.7 输出样例二
1196

 


2.8 输入样例三
20 10
0 0 1 1 3 2 3 0 1 7 5 9 4 0 6 15 14 10 15


2.9 输出样例三
540101309

 


2.10 数据范围与约定
对于100% 的数据,n<=80,k<n。

 

当时考试的时候先做的T1和T3然后就没时间做这个题了,,,

但是可能当时我有时间也做不出来吧23333.

 

正解是矩阵树定理+高斯消元/拉格朗日插值法。

因为最多只能选k条不是原来树上的边,所以我们可以把树边看成1,把非树边看成x。

最后的答案就是矩阵树定理消出的多项式的x^k及之前的项的系数和。

 

这个当然可以暴力多项式乘法(因为多项式的项数不多但是有很多多项式要相乘,所以此时的NTT是没有暴力快的),

但是这样太慢了,大数据会挂。

 

我们还可以发现最后的多项式是一个n-1次多项式,所以我们可以带n对点来确定这个多项式。

这样的复杂度是 O(N^4) ,开开心心过本题。。。

 

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=87;
const int ha=1000000007;
int n,k,a[maxn][maxn];
int fa,b[maxn][maxn];
int c[maxn][maxn],ans;

inline int add(int x,int y){
	x+=y;
	return x>=ha?x-ha:x;
}

inline int ksm(int x,int y){
	int an=1;
	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
	return an;
}

inline int matrix_tree(int x){
	int an=1;
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++)
	    for(int j=i+1;j<=n;j++){
	    	if(a[i][j]) b[i][j]=b[j][i]=ha-1,b[i][i]++,b[j][j]++;
	    	else b[i][j]=b[j][i]=ha-x,b[i][i]+=x,b[j][j]+=x;
		}
	
	for(int i=1;i<n;i++){
		int tmp=i;
		for(;tmp<n;tmp++) if(b[tmp][i]) break;
		if(tmp>i){
			an=ha-an;
			for(int k=i;k<n;k++) swap(b[i][k],b[tmp][k]);
		}
		
		for(int j=i+1;j<n;j++)
		    while(b[j][i]){
		    	an=ha-an;
		    	int A=b[i][i]/b[j][i];
		    	for(int k=i;k<n;k++){
		    		b[i][k]=((ll)b[i][k]-b[j][k]*(ll)A)%ha;
		    		if(b[i][k]<0) b[i][k]+=ha;
		    		swap(b[i][k],b[j][k]);
				}
			}
		
		an=an*(ll)b[i][i]%ha;
	}
	
	return an;
}

inline void xy(){
	n++;
	for(int i=1;i<n;i++){
		if(!c[i][i]){
			for(int j=i+1;j<n;j++) if(c[j][i]){
				for(int k=i;k<=n;k++) swap(c[j][k],c[i][k]);
				break;
			}
		}
		
		for(int j=i+1;j<n;j++)
		    while(c[j][i]){
		    	int A=c[i][i]/c[j][i];
		    	for(int k=i;k<=n;k++){
		    		c[i][k]=((ll)c[i][k]-c[j][k]*(ll)A)%ha;
		    		if(c[i][k]<0) c[i][k]+=ha;
		    		swap(c[i][k],c[j][k]);
				}
			}
	}
	
	for(int i=n-1;i;i--){
		for(int j=n-1;j>i;j--) c[i][n]=add(c[i][n],add(ha,-c[j][n]*(ll)c[i][j]%ha));
		c[i][n]=c[i][n]*(ll)ksm(c[i][i],ha-2)%ha;
	}
}

int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	
	scanf("%d%d",&n,&k);
	for(int i=2;i<=n;i++){
		scanf("%d",&fa);
		fa++,a[i][fa]++,a[fa][i]++;
	}
	
	for(int i=1;i<=n;i++){
		for(int j=1,u=1;j<=n;j++,u=u*(ll)i%ha) c[i][j]=u;
		c[i][n+1]=matrix_tree(i);
	}
	
	xy();
	
	k++;
	for(int i=1;i<=k;i++) ans=add(ans,c[i][n]);
	
	printf("%d\n",ans);
	
	return 0;
}

  

posted @ 2018-03-05 09:46  蒟蒻JHY  阅读(277)  评论(0编辑  收藏  举报