看了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
搜索
最新评论
|
|