NOI2008 生成树计数

    技术2024-11-18  15

     

    2011/2/8

    生成树计数

     

    题目我就不放了。 

     

    栋栋十分xe的把大家往行列式上面引,但是是不能用行列式做的。观察这道题的特点,k特别小,因此这肯定是切入点。所以用状态f[I,j]表示前i个,联通状态为j(为长度为k的最小表示)的方案数。依次处理从左到右的点,往状态右边加入一个点,并往左边的点连边,删去原来最左边的点。可行的连边是使得原来最左边的元素的联通块有延续,注意这个时候要对状态重新最小表示法,重新标号,因为关心的只是连通性。而不是各自的标号。

    初始的状态的方案数的处理:每个联通块都是独立不互相干扰的,那么对于每种标号的联通块,因为待选边集是完全图,用n个点的完全图的生成树个数为n^(n-2)来算。

     

    由于我一开始有问题,所以只压了k-1长度的位。中间有一个把k加一的东西,大家注意。有点丑。

     

     

    program xqz; const maxn=60; mo=65521; type arr1=array[0..6] of longint; arr2=array[0..maxn,0..maxn] of int64; var i,j,k,m,kk,now,tot:longint; n:int64; ans,a:arr2; num:array[0..100000] of longint; q:array[0..maxn] of longint; z,b,c,yk,f:arr1; ok:boolean; procedure dfs1(c:longint); var i:longint; begin if c=kk then begin inc(m); num[now]:=m; q[m]:=now; exit; end; for i:=tot downto 1 do begin now:=now*10+i; dfs1(c+1); now:=now div 10; end; inc(tot); now:=now*10+tot; dfs1(c+1); now:=now div 10; dec(tot); end; procedure add(now:longint; c:arr1); var i,k,m:longint; begin ok:=false; for i:=2 to kk do if c[i]=c[1] then ok:=true; if z[c[1]]=1 then ok:=true; if not ok then exit; k:=0; fillchar(yk,sizeof(yk),0); m:=0; for i:=kk downto 2 do begin if z[c[i]]=1 then c[i]:=kk; if yk[c[i]]=0 then begin inc(m); yk[c[i]]:=m; end; c[i]:=yk[c[i]]; k:=k*10+c[i]; end; inc(a[num[k],num[now]]); end; procedure dfs2(now,dep:longint); begin if dep=kk then begin add(now,c); exit; end; if z[b[dep]]=0 then begin c[dep]:=b[dep]; dfs2(now,dep+1); c[dep]:=kk; z[b[dep]]:=1; dfs2(now,dep+1); z[b[dep]]:=0; end else begin c[dep]:=kk; dfs2(now,dep+1); end; end; procedure link(now:longint); var i,k:longint; begin k:=now; for i:=1 to kk-1 do begin b[i]:=k mod 10; k:=k div 10; end; c[kk]:=kk; dfs2(now,1); end; procedure cheng(var a:arr2;b,c:arr2); var i,j,k:longint; begin for i:=1 to m do for j:=1 to m do begin a[i,j]:=0; for k:=1 to m do inc(a[i,j],b[i,k]*c[k,j]); a[i,j]:=a[i,j] mod mo; end; end; procedure cal(n:int64); begin if n>1 then begin cal(n shr 1); cheng(ans,ans,ans); if odd(n) then cheng(ans,ans,a); end else ans:=a; end; function ff(k:longint):longint; var i,now:longint; begin fillchar(yk,sizeof(yk),0); now:=1; for i:=1 to kk-1 do begin inc(yk[k mod 10]); k:=k div 10; end; for i:=1 to kk-1 do now:=now*f[yk[i]]; ff:=now; end; begin assign(input,'count.in'); reset(input); assign(output,'count.out'); rewrite(output); read(kk,n); if n<kk then begin now:=1; for i:=1 to n-2 do now:=now*n mod mo; writeln(now); end else begin inc(kk); //!!! now:=0; tot:=0; dfs1(1); for i:=1 to m do link(q[i]); cal(n-kk+1); for i:=0 to kk-1 do begin f[i]:=1; for j:=1 to i-2 do f[i]:=f[i]*i; end; now:=0; k:=0; for i:=1 to m do k:=(k+ans[1,i]*ff(q[i])) mod mo; writeln(k); end; close(input); close(output); end.  

    最新回复(0)