题目:点击打开链接
题意:给两个数列a[n],b[m],要求满足一个函数f(i)=bf(a[i])。定义域0-n,值域0-m
解释样例2:a={2,0,1},b={0,2,3,1}
f(0)=bf(2)
f(1)=bf(0)
f(2)=bf(1),这个例子的可以取得可能有,令f(2)=0,根据上式推得f(0),f(1)都为0;令f(2)=2,3,1,也容易推出该函数成立。所以ans=4。
思路:根据样例的推法,容易看出本题通过找出a序列的循环节个数以及每个循环节长度,b序列对应a每个循环节长度或者长度约数的循环节个数,答案就是b序列满足条件的循环节 其个数*长度 求和。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
int main()
{
int n,m,a[100002],b[100002],numa[100002],numb[100002];
int cas=0;
while(~scanf("%d%d",&n,&m))
{
memset(numa,0,sizeof(numa));
memset(numb,0,sizeof(numb));
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
ll tt=0;
for(int i=0;i<n;i++)
{
int k=i,tot=0;
while(a[k]!=-1)
{
int t=k;
k=a[k];
tot++;
a[t]=-1;
}
if(tot) numa[++tt]=tot;//numa[i]记录第i个循环节的循环长度
}
for(int i=0;i<m;i++)
scanf("%d",&b[i]);
for(int i=0;i<m;i++)
{
int k=i,tot=0;
while(b[k]!=-1)
{
int t=k;
k=b[k];
tot++;
b[t]=-1;
}
if(tot) numb[tot]++;//numb[i]记录循环长度为i的循环节个数
}
ll ans=1;
for(int i=1;i<=tt;i++)
{
ll ansi=0;
for(int j=1;j*j<=numa[i];j++)
{
if(numa[i]%j==0)
{
if(j*j==numa[i])
{
ansi+=numb[j]*j;
}
else
ansi+=numb[j]*j+numb[numa[i]/j]*numa[i]/j;
}
}
ans=(ans*ansi)%mod;
}
cas++;
printf("Case #%d: %lld\n",cas,ans);
}
return 0;
}