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

26 篇文章 0 订阅

考场上神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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值