题意:给出两个排列,a和b,求满足映射的f[i] = bf[a[i]]的个数。
思路:
one. 列出所有式子,进行连边,一个环中某一个元素一旦确定,其它也就确定了,所以需要求强连通分量,要想能够构成一个k个元素的强连通分量,只有m个数中的连环数个数为k的因子时才能够构成。然后再对所有连通分量的方案数进行乘积。
two. 用置换群的思想,通过f函数构造一个新的置换,然后找到构造的置换和m个数置换的轮换即一个环进行操作即可。
代码1:
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <map>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
const int maxn = 1e5+5;
const int maxm = maxn;
struct node
{
int v, next;
} edge[maxm];
int no, head[maxn];
int n, m;
int index, top, num[maxn], low[maxn], book[maxn], S[maxn];
int a[maxn], b[maxn];
int vis[maxn], circle[maxn];
map<int, int> mp;
map<int, int>::iterator it;
LL ans, final;
inline void init()
{
mp.clear();
no = 0; index = 0; top = 0;
memset(head, -1, sizeof head);
memset(book, 0, sizeof book);
memset(circle, 0, sizeof circle);
memset(num, 0, sizeof num);
memset(vis, 0, sizeof vis);
}
inline void add(int u, int v)
{
edge[no].v = v; edge[no].next = head[u];
head[u] = no++;
}
void tarjan(int cur)
{
num[cur] = low[cur] = ++index;
vis[cur] = 1; S[++top] = cur;
for(int k = head[cur]; k != -1; k = edge[k].next)
{
if(!num[edge[k].v])
{
tarjan(edge[k].v);
low[cur] = min(low[cur], low[edge[k].v]);
}
else if(vis[edge[k].v])
{
low[cur] = min(low[cur], num[edge[k].v]);
}
}
if(num[cur] == low[cur])
{
int cnt = 0;
do
{
++cnt;
vis[S[top]] = 0;
--top;
}
while(S[top+1] != cur);
++mp[cnt];
}
}
int main()
{
int count = 0;
while(~scanf("%d %d", &n, &m))
{
init(); final = 1;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int i = 0; i < m; ++i) scanf("%d", &b[i]);
for(int i = 0; i < m; ++i)
{
if(book[i]) continue;
int k = b[i], cnt = 1;
while(k != i) ++cnt, k = b[k];
k = b[i]; book[i] = 1; ++circle[cnt];
while(k != i) ++circle[cnt], book[k] = 1, k = b[k];
}
for(int i = 0; i < n; ++i) add(a[i], i);
for(int i = 0; i < n; ++i) if(!num[i]) tarjan(i);
for(it = mp.begin(); it != mp.end(); ++it)
{
int i, k = it->first, t = it->second;
ans = 0;
ans = (ans+(LL)circle[1])%mod;
for(i = 2; i*i < k; ++i)
{
if(k%i == 0)
{
ans = (ans+(LL)circle[i])%mod;
ans = (ans+(LL)circle[k/i])%mod;
}
}
if(k%i==0 && k/i == i) ans = (ans+(LL)circle[i])%mod;
if(k > 1) ans = (ans+(LL)circle[k])%mod;
for(int j = 1; j <= t; ++j) final = final*ans%mod;
}
printf("Case #%d: %lld\n", ++count, final);
}
return 0;
}
代码2:
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <map>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
const int maxn = 1e5+5;
int n, m;
int a[maxn], b[maxn], gz[maxn];
int vis[maxn], cir1[maxn];
map<int, int> mp;
map<int, int>::iterator it;
LL ans, final;
int main()
{
int count = 0;
while(~scanf("%d %d", &n, &m))
{
memset(cir1, 0, sizeof cir1);
memset(vis, 0, sizeof vis); final = 1;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
for(int i = 0; i < m; ++i) scanf("%d", &b[i]);
for(int i = 0; i < m; ++i)
{
if(vis[i]) continue;
int k = b[i], cnt = 1;
while(k != i) ++cnt, k = b[k];
k = b[i]; vis[i] = 1; ++cir1[cnt];
while(k != i) ++cir1[cnt], vis[k] = 1, k = b[k];
}
memset(vis, 0, sizeof vis); mp.clear();
for(int i = 0; i < n; ++i) gz[a[i]] = i;
for(int i = 0; i < n; ++i)
{
if(vis[i]) continue;
int k = gz[i], cnt = 1;
while(k != i) ++cnt, k = gz[k];
k = gz[i]; vis[i] = 1;
while(k != i) vis[k] = 1, k = gz[k];
++mp[cnt];
}
for(it = mp.begin(); it != mp.end(); ++it)
{
int i, k = it->first, t = it->second;
ans = 0;
ans = (ans+(LL)cir1[1])%mod;
for(i = 2; i*i < k; ++i)
{
if(k%i == 0)
{
ans = (ans+(LL)cir1[i])%mod;
ans = (ans+(LL)cir1[k/i])%mod;
}
}
if(k%i==0 && k/i == i) ans = (ans+(LL)cir1[i])%mod;
if(k > 1) ans = (ans+(LL)cir1[k])%mod;
for(int j = 1; j <= t; ++j) final = final*ans%mod;
}
printf("Case #%d: %lld\n", ++count, final);
}
return 0;
}
继续加油~