【51NOD1806】wangyurzee的树(Prufer编码,容斥原理,组合计数)

题意:有n个点和m条限制,每条限制限制了一个点的度数不能为某个数。

求合法的树的个数模10^9+7

n<=10^6

m<=17

思路:WYZ作业

首先m<=17显然是2^m容斥

枚举每一个限制有用或没用,考虑某一个约束情况下的方案数

Caylay定理:n个点的生成树的个数=n^(n-2)

Prufer序列:一个长度为n-2的Prufer序列对应唯一的一棵n个节点的树,度数为a[i]的点在其中出现了(a[i]-1)次

考虑先在序列中填上所有的受约束条件的点,它们的方案数是C(n-2,a[1]-1)*C(n-2-(a[1]-1),a[2]-1)*……

化简得:

(n-2)!/((a[1]-1)!(a[2]-1)!……*(n-2-sigma(a[i]-1))!)

考虑没有约束的点可以随意在剩下的序列中出现,它们的方案数为:

(n-p)^(n-2-sigma(a[i]-1))

其中p为选中的约束条件个数

将两部分相乘,根据p的奇偶性进行容斥计算即可

几个hack点:

1.对于同一个点可能会有多个约束条件,需要判断某一个点是否被多次限制,如果被多次限制就不能计入答案

2.n=1需要特判ans=1(真的吗,这个点可是坑了我5点头盾

 1 const mo=1000000007;
 2 var flag:array[1..1000000]of longint;
 3     fac,exf:array[0..1000000]of int64;
 4     a,b,c:array[1..17]of longint;
 5     n,m,i:longint;
 6     sun:int64;
 7 
 8 function mult(x,y:longint):int64;
 9 var tmp:int64;
10 begin
11  tmp:=x; mult:=1;
12  while y>0 do
13  begin
14   if y and 1=1 then mult:=mult*tmp mod mo;
15   tmp:=tmp*tmp mod mo;
16   y:=y>>1;
17  end;
18 end;
19 
20 procedure dfs(k:longint);
21 var i,j,tot,sum:longint;
22     ans:int64;
23 begin
24  if k=m+1 then
25  begin
26   tot:=0; ans:=fac[n-2]; sum:=n-2;
27   for i:=1 to m do
28    if c[i]>0 then
29    begin
30     inc(tot);
31     inc(flag[a[i]]);
32     sum:=sum-(b[i]-1);
33    end;
34   for i:=1 to m do
35    if (c[i]>0)and(flag[a[i]]>1) then
36    begin
37     for j:=1 to m do
38      if c[j]>0 then dec(flag[a[j]]);
39     exit;
40    end;
41   for i:=1 to m do
42    if c[i]>0 then ans:=ans*exf[b[i]-1] mod mo;
43   //writeln(sum);
44   if sum>=0 then
45   begin
46    ans:=ans*exf[sum] mod mo;
47    ans:=ans*mult(n-tot,sum) mod mo;
48    if tot and 1=0 then sun:=(sun+ans) mod mo
49     else sun:=(sun-ans) mod mo;
50    sun:=(sun+mo) mod mo;
51   end;
52   for i:=1 to m do
53    if c[i]>0 then dec(flag[a[i]]);
54   exit;
55  end;
56  if b[k]=0 then dfs(k+1)
57   else
58   begin
59    c[k]:=1;
60    dfs(k+1);
61    c[k]:=0;
62    dfs(k+1);
63   end;
64 end;
65 
66 begin
67  assign(input,'51nod1806.in'); reset(input);
68  assign(output,'51nod1806.out'); rewrite(output);
69  readln(n,m);
70  if n=1 then
71  begin
72   writeln(1);
73   exit;
74  end;
75  for i:=1 to m do read(a[i],b[i]);
76  exf[0]:=1; exf[1]:=1; fac[0]:=1;
77  for i:=1 to 1000000 do fac[i]:=fac[i-1]*i mod mo;
78  for i:=2 to 1000000 do exf[i]:=exf[mo mod i]*(mo-mo div i) mod mo;
79  for i:=1 to 1000000 do exf[i]:=exf[i-1]*exf[i] mod mo;
80  dfs(1);
81  writeln(sun);
82  close(input);
83  close(output);
84 end.
原文地址:https://www.cnblogs.com/myx12345/p/6624038.html