解法:这道题很有意思,值得一做和细细思考。
首先是我们要观察这个映射公式,他的实质是什么?其实就是b到a的循环映射,这是因为a数列和b数列都是0-n-1的排列,意味着每个数都是唯一的,那么ab的这个循环映射就是循环的封闭的不会影响到其他数。这样的话b到a的映射就形成了一个个的循环节。
那么我们考虑先分别把a数组和b数组的循环节找出来,此时我们要思考怎样才能满足这样的循环映射呢?
答案就是a的循环节大小要是b循环节大小的倍数!!!这个有点难解释,如果不是倍数关系的话会出现:b开始映射a,b的第一轮映射没事,当第一轮完了之后就会出现不匹配的问题,这是因为b不是a的因子。
那么答案就出来了,对于每某一个b的循环节lena只要它是某一个a的循环节lena的因子l就能对lena造成lenb的贡献(意思是开始映射的位置是不关键的,关键的是大小)。那么我们就用类似与埃氏筛素数的办法计算每一个lenb对其倍数的贡献,之后枚举lena累乘贡献即可。
代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e5+10; 4 const int P=1e9+7; 5 int n,m; 6 int a[N],b[N],c[N],na[N],nb[N]; 7 8 bool vis[N]; 9 void circle(int *a,int n,int *na) { 10 for (int i=0;i<=n;i++) vis[i]=0; 11 for (int i=0;i<n;i++) { 12 if (vis[i]) continue; 13 vis[i]=1; 14 int len=1,now=i; 15 while (!vis[a[now]]) { 16 vis[a[now]]=1; 17 now=a[now]; 18 len++; 19 } 20 na[++na[0]]=len; 21 } 22 } 23 24 int main() 25 { 26 int cas=0; 27 while (scanf("%d%d",&n,&m)==2) { 28 for (int i=0;i<n;i++) scanf("%d",&a[i]); 29 for (int j=0;j<m;j++) scanf("%d",&b[j]); 30 na[0]=nb[0]=0; 31 circle(a,n,na); circle(b,m,nb); 32 33 for (int i=0;i<=n;i++) c[i]=0; 34 for (int i=1;i<=nb[0];i++) 35 for (int j=nb[i];j<=n;j+=nb[i]) c[j]+=nb[i]; 36 37 long long ans=1; 38 for (int i=1;i<=na[0];i++) ans=(ans*c[na[i]])%P; 39 printf("Case #%d: %lld ",++cas,ans); 40 } 41 return 0; 42 }