题意:有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.