[雅礼4-7]T1 prufer序列&&dp

来源:互联网 发布:淘宝买手机卡认证过程 编辑:程序博客网 时间:2024/05/04 23:37

考场上神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.
0 0