1806 wangyurzee的树
基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 收藏 关注
wangyurzee有n个各不相同的节点,编号从1到n。wangyurzee想在它们之间连n-1条边,从而使它们成为一棵树。
可是wangyurzee发现方案数太多了,于是他又给出了m个限制条件,其中第i个限制条件限制了编号为u[i]的节点的度数不能为d[i]。
一个节点的度数,就是指和该节点相关联的边的条数。
这样一来,方案数就减少了,问题也就变得容易了,现在请你告诉wangyurzee连边的方案总数为多少。
答案请对1000000007取模。
样例解释
总方案共有3种,分别为{(1,2),(1,3)},{(1,2),(2,3)},{(2,3),(1,3)}。其中第二种方案节点1的度数为2,不符合要求,因此答案为2。
Input
第一行输入2个整数n(1<=n<=1000000),m(0<=m<=17)分别表示节点个数以及限制个数。
第2行到第m+1行描述m个限制条件,第i+1行为2个整数u[i],d[i],表示编号为u[i]的节点度数不能为d[i]。
为了方便起见,保证1<=ui<=m。同时保证1<=ui<=n,1<=di<=n-1,保证不会有两条完全相同的限制。
Output
输出一行一个整数表示答案。
Input示例
3 1
1 2
Output示例
2
这题首先要知道prufer编码,对于一个n个点的图,它的生成树的个数是
nn−2
,然后这题还对一些点有度数的要求,但是只有17个点,所以可以容斥,然后prufer编码有个推广,n个节点的度依次为D1, D2, …, Dn的无根树共有(n-2)! / [ (D1-1)!(D2-1)!..(Dn-1)! ]个,因为此时Prüfer编码中的数字i恰好出现Di-1次。
对于这题,就是告诉你这会你容斥的x个点的度数和是sum,然后公式就是
(n−x)n−2−(sum−x)∗(n−2)!(n−2−(sum−x))!(D1−1)!(D2−1)!..(Dx−1)!
注意这题还有个坑点就是可能给你的条件里有个同一个点不能有两个度数的要求,所以容斥的时候一定不能出现重复的同一个点
代码:
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1000005
#define MAXN 1000005
#define maxnode 205
#define sigma_size 26
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-4;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
LL fact[MAX];
pii x[20];
void init(){
fact[0]=1;
for(int i=1;i<=1000000;i++) fact[i]=fact[i-1]*i%mod;
}
LL qpow(LL a,LL n){
LL ans=1;
while(n){
if(n&1) ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
init();
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
x[i]=mk(a,b);
}
sort(x,x+m);
if(n==1){
printf("1\n");
return 0;
}
LL ans=qpow(n,n-2);
for(int i=1;i<(1<<m);i++){
int flag=0;
int k=0;
int cnt=0;
for(int j=0;j<m;j++){
if((i>>j)&1){
if(k!=x[j].first) k=x[j].first;
else{
flag=1;
break;
}
cnt+=x[j].second-1;
if(n-2-cnt<0){
flag=1;
break;
}
}
}
if(flag) continue;
LL tmp=fact[n-2];
int num=0;
for(int j=0;j<m;j++){
if((i>>j)&1){
num++;
tmp=tmp*qpow(fact[x[j].second-1],mod-2)%mod;
}
}
LL ret=(qpow(n-num,n-2-cnt)*tmp%mod)*qpow(fact[n-2-cnt],mod-2)%mod;
if(num%2) ans-=ret;
else ans+=ret;
}
cout<<(ans%mod+mod)%mod<<endl;
return 0;
}