Description
题目背景
热烈庆祝北京师范大学附属实验中学成立100周年!
问题描述
经过一天的忙碌,志愿者们结束了他们的工作,准备站在一排合影留念。
现在总共有n名志愿者留下来准备合影。不过,进程并不是那么顺利,有些同学提出了一些奇奇怪怪的要求(每个人最多只会提出一个):他必须站在另外一个同学的左边(不一定相邻),仁慈的老师满足了他们的要求。这时,其中一位来自11班的同学小Z陷入了沉思:总共有多少种不同的合法方案数呢?(两种方案不同当且仅存在至少一名同学他在这两个方案当中站的位置不同。)小Z很快就算出来了,于是就把自己的这个问题告诉了好朋友小C。不过,由于小C的数学功底不足,小Z只要求他算出这个答案模质数p的余数就可以了。可就算这样,小C也不会做。为了显示自己的水平很高(实际上很低),他找到了你,并把你得出的答案报给小Z,所以你可一定要算对啊!
Input
第一行包含一个整数T,表示数据组数
对于每一组数据,第一行两个整数n, m, p,分别表示志愿者数、奇奇怪怪的要求的个数和模数,保证p为质数。
接下来m行,每行两个整数x,y ,表示x必须在y左边。
Output
输出总共T行,第i行的数为第i组询问对应的答案ansi
Sample Input
2
3 1 17
1 2
5 0 101
Sample Output
3
19
样例说明
对于第一组询问,总共三种方案:
123 132 312
对于第二组询问:由于没有限制,所以总共有5!=120种不同的方案,模101后是19
Data Constraint
对于15%的数据,n,m<=9
对于30%的数据, n,m<=17
对于50%的数据, n,m<=20
对于70%的数据, n,m<=2000
对于100%的数据, n<=2*10^5,m<=2*10^5,m<=n,n+10<=p<=10^9+7,T<=10
伪题解
其实这题做法很简单
只需要写一个dfs暴力,跑上个30组数据
用n!除以答案,再分解质因数
再看上个30min,这题就看出来了(逃
其实就是算出来每个点的子树大小
然后用n!除以他们的乘积,然后就是答案了!
(话说我也不知道这题为什么可以这样做)
对了,还要判环,有环则无解
(ps:其实正解是dp+组合数)
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=' ';
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
ll p;
inline ll ksm(ll a,ll b){
a%=p;
ll ans=1;
while(b){
if(b&1){
ans=(ans*a)%p;
}
a=(a*a)%p;
b>>=1;
}
return ans;
}
int T;
struct edge{
int to,next;
}e[200001];
int n,m,tot;
int head[200001];
inline void addedge(int x,int y){
e[++tot].to=y;e[tot].next=head[x];head[x]=tot;
}
int vis[200001],size[200001];
int in[200001];
inline void toposort(){
queue<int> q;
for(int i=1;i<=n;i++){
if(in[i]==0)q.push(i);
size[i]=1;
}
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int u=e[i].to;
if(vis[u])continue;
--in[u];
size[u]+=size[x];
if(in[u]==0)q.push(u);
}
}
}
ll prime[250001];
ll c[250001];
ll vs[250001];
int cnt;
int main(){
freopen("photo.in","r",stdin);
freopen("photo.out","w",stdout);
T=read();
for(int i=2;i<=250000;++i){
if(!vs[i]){
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<=250000;++j){
vs[i*prime[j]]=1;
if(i%prime[j]==0)break;
}
}
while(T--){
tot=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(size,0,sizeof(size));
memset(c,0,sizeof(c));
memset(in,0,sizeof(in));
n=read();m=read();p=read();
for(int i=1;i<=m;++i){
int x=read(),y=read();
addedge(x,y);
in[y]++;
}
toposort();
int flag=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
printf("0\n");
flag=1;
break;
}
}
if(flag)continue;
for(int i=1;i<=cnt&&prime[i]<=n;++i){
ll now=prime[i];
while(now<=n){
c[i]+=n/now;
now*=prime[i];
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=cnt&&prime[j]<=size[i];++j){
while(size[i]%prime[j]==0){
--c[j];
size[i]/=prime[j];
}
}
}
ll ans=1;
for(int i=1;i<=cnt&&prime[i]<=n;++i){
ans=(ans*ksm(prime[i],c[i]))%p;
}
printf("%lld\n",ans);
}
return 0;
}