题意:n个点,允许任意两点连边,给出某些点最终的度数,求所有满足要求的树的个数
先介绍prufer编码:
(一)将树转成prufer编码
任意一棵n个点的树都会转成长度为(n-2)prufer编码
度数为m的点,在prufer编码中出现的次数为m-1
第i布时,删去叶子节点中标号最小的点及与它相连的边,并把与它相邻的点加入prufer数列
(二)将prufer编码转成树
设{a1,a2,..an-2}为一棵有n个节点的树的Prufer序列,同时新建集合G{1..n},找出G集合中未在Prufer序列中出现过的最小的数,将该点与Prufer序列中首项连一条边,并将该点和Prufer序列首项删除,重复操作n-2次,最后将G集合中剩余的两个点之间连边即可。
然后我们回归题目
我们用d[i]表示i点要求的最终度数
令 tot=sigma(d[i]-1) (d[i]<>-1) ; m=sigma(1) (d[i]=-1)
根据定义,当tot>n-2 或者 当m=0且tot!=n-2 时,是不合法的,无解
根据排列组合可知 ans=C(n-2,tot)*C(tot,d1-1)*C(tot-d1+1,d2-1)*...*C(dn-1,dn-1)*m^(n-2-tot)
化简可得 ans=(n-2)!*m^(n-2-tot)/((n-2-tot)!*(d1-1)!*(d2-1)!*..*(dn-1)!)
然后蒟蒻发现要写高精除果断弃疗...
然后果断%%%hzwer(一下截自黄学长博客)
var
n,m,tot,x :longint;
i,j :longint;
vis :array[0..1010] of boolean;
prime :array[0..1010] of longint;
up,down,mid :array[0..1010] of longint;
ans :array[0..500010] of longint;
procedure pre_do;
var
i:longint;
begin
for i:=2 to n do
begin
if not vis[i] then
begin
inc(prime[0]);
prime[prime[0]]:=i;
end;
for j:=1 to prime[0] do
if i*prime[j]>n then break else
begin
vis[i*prime[j]]:=true;
if i mod prime[j]=0 then break;
end;
end;
end;
procedure calcdown(x:longint);
var
tt,i:longint;
begin
for i:=1 to prime[0] do
begin
if prime[i]>x then exit;
tt:=prime[i];
while tt<=x do
begin
inc(down[i],x div tt);
tt:=tt*prime[i];
end;
end;
end;
procedure calcup(x:longint);
var
tt,i:longint;
begin
for i:=1 to prime[0] do
begin
if prime[i]>x then exit;
tt:=prime[i];
while tt<=x do
begin
inc(up[i],x div tt);
tt:=tt*prime[i];
end;
end;
end;
procedure cheng(x:longint);
var
i,tt:longint;
begin
for i:=1 to ans[0] do ans[i]:=ans[i]*x;
tt:=0;
for i:=1 to ans[0] do
begin
inc(ans[i],tt);
tt:=ans[i] div 10000;
ans[i]:=ans[i] mod 10000;
end;
while tt<>0 do
begin
inc(ans[0]);
ans[ans[0]]:=tt mod 10000;
tt:=tt div 10000;
end;
end;
procedure qc(a,b:longint);
begin
if b=0 then exit;
if (b and 1=1) then cheng(a);
if b=1 then exit;
qc(a,b>>1);
qc(a,b>>1);
end;
begin
read(n);
pre_do;
for i:=1 to n do
begin
read(x);
if x=-1 then inc(m) else
begin
inc(tot,x-1);
calcdown(x-1);
end;
end;
//
if (tot>n-2) or ((m=0) and (tot<>n-2)) then
begin
writeln(0); exit;
end;
//
if n-2-tot<>0 then calcdown(n-2-tot);
if n-2<>0 then calcup(n-2);
ans[0]:=1; ans[1]:=1;
for i:=1 to prime[0] do mid[i]:=up[i]-down[i];
for i:=1 to prime[0] do
if mid[i]>0 then qc(prime[i],mid[i]);
qc(m,n-2-tot);
//
write(ans[ans[0]]);
for i:=ans[0]-1 downto 1 do
begin
if ans[i]<1000 then write(0);
if ans[i]<100 then write(0);
if ans[i]<10 then write(0);
write(ans[i]);
end;
writeln;
end.
——by Eirlys