考场上神tm忘了可以乘逆元结果复杂度变成n^5,T了两个点。。。
说说我自己的做法吧,首先一棵树和它的prufer序列是一一对应的,而且一个度数为w的点在序列中出现了w-1次。于是我们针对prufer序列来设计状态。f[i,j,k]表示枚举到第i个点,选了其中j个点进序列,使用了k个序列空间,于是f[i, j, k]=f[i-1, j, k]+ sigma {f[i-1, j+1, k-t]* C(n-zero-(k-t), t) } (1<=t<=a[i]),组合数的意义就是乘上说在剩下的序列空间中任选t个的方案数,其中zero表示a[x]=0的x的个数。
最终统计答案的时候,ans[k+2]=sigma{dp[n, j, k]* C(n-j, k+2-j)} (1<=j<=k) /C(n-2, k) 。前一个组合数的意义是可以任意选一些没在prufer序列中出现的点充当树中度为0的点,后一个组合数是因为最初计算的是当成n-2个序列空间计算的,所以要除掉n-2中选k个序列空间的方案数。
状态O(n^3),转移O(n),总时间复杂度O(n^4)。
代码:
var
c:array[-60..60,-60..60]of int64;
ca,tt,i,j,k,l,n,z,num:longint;
a:array[0..60]of longint;
dp:array[-1..60,-1..60,-1..60]of int64;
ans:array[0..60]of int64;
const md=1000000007;
function min(x,y:longint):longint;
begin
if x>y then exit(y)
else exit(x);
end;
function ksm(a,b:int64):int64;
begin
ksm:=1;
while b>0 do
begin
if b mod 2=1 then ksm:=(ksm*a)mod md;
a:=(a*a)mod md;
b:=b div 2;
end;
end;
begin
c[0,0]:=1;
for i:=1 to 50 do
for j:=0 to i do
c[i,j]:=(c[i-1,j]+c[i-1,j-1])mod md;
readln(ca);
for tt:=1 to ca do
begin
fillchar(dp,sizeof(dp),0);
fillchar(ans,sizeof(ans),0);
readln(n);
z:=0;
for i:=1 to n do
begin
read(a[i]);
if a[i]=0 then inc(z);
end;
j:=0;
for i:=1 to n do
if a[i]<>0 then begin inc(j); a[j]:=a[i]; end;
n:=n-z;
fillchar(dp,sizeof(dp),0);
dp[0,0,0]:=1;
for i:=1 to n do
begin
for j:=0 to i do
begin
for k:=j to n-2 do
begin
dp[i,j,k]:=dp[i-1,j,k];
for l:=1 to min(k,a[i]-1) do
dp[i,j,k]:=dp[i,j,k]+(dp[i-1,j-1,k-l]*c[n-2-(k-l),l])mod md;
dp[i,j,k]:=dp[i,j,k]mod md;
end;
end;
end;
for j:=0 to n do
for k:=j to n-2 do
ans[k+2]:=(ans[k+2]+dp[n,j,k]*c[n-j,k+2-j] mod md *ksm(c[n-2,k],md-2) mod md)mod md;
ans[1]:=n;
for i:=1 to n+z do
write(ans[i],' ');
writeln;
end;
end.