看了watashi的。。。Orz
 /**//*
题目说的就是你有m种交换方式(所以可以使用多次),用a交换别人的b,这可以看成一个a到b的有向边,题目保证了
每个顶点的入度和出度都不超过1。题目问的是有多少种不同的序列,能够通过变换,
最后成为一个n的排列。

注意每个顶点的入度和出度都不超过1,所以每个联通分量只有可能是链或环。
对于环的情况,显然就可以任意分配了,所以环内部任意交换的顺序有m^m种。
对于链的情况,这是一个非常经典的问题,答案是(m+1)^(m-1)种
最后统计各部分,用组合公式选择位置然后都乘起来

对于链的,CII 4301 ipsc 2009 G
*/
#include<cstdio>
#include<cstring>

const int MAXN = 211;
const long long MOD = 1000000007;

int next[MAXN],in[MAXN];
bool vi[MAXN];
long long C[MAXN][MAXN];

void init()
  {
C[0][0] = 1;
for(int i = 1;i<MAXN;i++)
 {
C[i][0] = C[i][i] = 1;
for(int j = 1;j<i;j++)
C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
}
}

int dfs(int u)
  {
vi[u] = true;
int ans = 1;
if(next[u] != -1 && vi[next[u]] == false) ans += dfs(next[u]);
return ans;
}

long long ipow(int a,int n)
  {
if(n == 0) return 1;
if(n == 1)return a;
long long ans = ipow(a,n/2);
ans = ans * ans % MOD;
if(n&1)ans = ans * a % MOD;
return ans;
}
int main()
  {
//freopen("in","r",stdin);
init();
int T;
for(scanf("%d",&T);T--;)
 {
int N,M;
scanf("%d%d",&N,&M);
for(int i=0;i<N;i++)
 {
next[i] = -1;
in[i] = 0;
vi[i] = 0;
}
for(int i=0,a,b;i<M;i++)
 {
scanf("%d%d",&a,&b);
in[b] ++;
next[a] = b;
}
long long ans = 1;
int left = N;
for(int i = 0; i< N;i++)
 {
if(in[i] == 0)
 {
int tot = dfs(i);
long long tmp = C[left][tot] * ipow(tot+1,tot-1) % MOD;
ans = ans * tmp % MOD;
left -= tot;
}
}
for(int i = 0;i< N ;i++)
 {
if(vi[i] == false)
 {
int tot = dfs(i);
long long tmp = C[left][tot] * ipow(tot,tot)% MOD;
ans = ans * tmp % MOD;
left -= tot;
}
}
printf("%lld\n",ans);
}
return 0;
}
|
|
常用链接
随笔分类
Links
搜索
最新评论

|
|