测试地址:Function
题目大意: 给出一个关于
0
0
0到
n
−
1
n-1
n−1的置换
a
a
a,一个关于
0
0
0到
m
−
1
m-1
m−1的置换
b
b
b,求有多少从
0
0
0到
n
−
1
n-1
n−1映射到
0
0
0到
m
−
1
m-1
m−1的映射
f
f
f,满足
f
(
i
)
=
b
f
(
a
i
)
f(i)=b_{f(a_i)}
f(i)=bf(ai)。
做法: 本题需要用到思维+组合数学。
根据题目的要求,
f
(
a
i
)
f(a_i)
f(ai)可以通过置换
b
b
b成为
f
(
i
)
f(i)
f(i),所以我们对于置换
a
a
a画一个反向循环图(即,从点
i
i
i可以走到点
a
i
a_i
ai,反向就是从点
a
i
a_i
ai走到点
i
i
i),再对置换
b
b
b画一个循环图,在两张图中分别选出两个点
x
,
y
x,y
x,y,代表
f
(
x
)
=
y
f(x)=y
f(x)=y,那么可以发现,当
x
x
x走一步,
y
y
y也走一步,
f
(
x
)
=
y
f(x)=y
f(x)=y应该还是成立的。这也就说明,
y
y
y在
b
b
b中的循环长度,应该是
x
x
x在
a
a
a中循环长度的因数,这样才能保证
f
(
x
)
=
y
f(x)=y
f(x)=y在走的过程中总是成立。而因为初始的
y
y
y又有
l
e
n
(
y
)
len(y)
len(y)种选择(
l
e
n
(
y
)
len(y)
len(y)即
y
y
y所在循环的长度),因此我们找到了一种统计答案的方法:对一个
a
a
a中的环,在
b
b
b中找到所有环长为这个环长的因数的环,累加
l
e
n
(
y
)
len(y)
len(y)。直接统计因数的贡献比较麻烦,我们反过来考虑每个
b
b
b中的环,它对环长为
l
e
n
(
y
)
len(y)
len(y)的倍数的环有
l
e
n
(
y
)
len(y)
len(y)贡献,我们只需要一开始算出
c
o
u
n
t
i
count_i
counti,表示环长为
i
i
i的
b
b
b中的环的个数,就可以
O
(
m
log
m
)
O(m\log m)
O(mlogm)计算贡献了。那么对于
a
a
a中的每个环,可以计算出这个环的方案数,运用乘法原理把每个环的方案数乘起来就行了。
我傻逼的地方:我又分不清楚
n
n
n和
m
m
m了…这错误一次比一次觉得傻逼…
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
int n,m,a[100010],b[100010],tot;
ll cnt[100010],tmp[100010];
bool vis[100010];
void find_loop(int *nxt,int v)
{
while(!vis[v])
{
vis[v]=1;tot++;
v=nxt[v];
}
}
int main()
{
int t=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
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++)
tmp[i]=vis[i]=0;
for(int i=0;i<m;i++)
if (!vis[i])
{
tot=0;
find_loop(b,i);
tmp[tot]++;
}
for(int i=1;i<=n;i++)
cnt[i]=0;
for(int i=1;i<=m;i++)
for(int j=1;i*j<=n;j++)
cnt[i*j]=(cnt[i*j]+tmp[i]*(ll)i)%mod;
ll ans=1;
for(int i=0;i<n;i++)
vis[i]=0;
for(int i=0;i<n;i++)
if (!vis[i])
{
tot=0;
find_loop(a,i);
ans=ans*cnt[tot]%mod;
}
printf("Case #%d: %lld\n",++t,ans);
}
return 0;
}