HDU-6038 Function 思维+循环节

 解法:这道题很有意思,值得一做和细细思考。

首先是我们要观察这个映射公式,他的实质是什么?其实就是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\n",++cas,ans);
40     }    
41     return 0;
42 } 

猜你喜欢

转载自www.cnblogs.com/clno1/p/11644037.html
今日推荐