2017多校第一套&&hdu6038 思维 数学

链接  http://acm.hdu.edu.cn/showproblem.php?pid=6038

 

题意: 
给你一个a序列,代表0到n-1的排列;一个b序列代表0到m-1的排列。问你可以找出多少种函数关系f,f的定义域内的i都满足f(i)=b[f(a[i])]; 
分析:这个主要是找循环节 循环节导致函数有多种情况 找到每段循环节的 取值 种数 相乘起来就是答案
比如说:如果 a 序列是 2 0 1 那么我们可以发现

f[0] = b[f(a[0])]     f[0]  = b[f(2)] 
f[1] = b[f(a[1])]     f[1]  = b[f(0)] 
f[2] = b[f(a[2])]     f[2]  = b[f(1)]

对于这组数来说,假如我们先指定了f(0)对应的在b中的值,那么根据第2个式子,就可以得出f(1),根据f(1)就又可以得出f(2),最后根据f(2)就可以检验f(0)的值是否正确。

仔细观察左边的柿子 (定义域)i与a[ i ] 是循环的 如果想使上述成立,必须右边的柿子(值域)i与b[ i ] 也存在其约数长度的循环节。 

如果不是约数长度的循环节会使上述假设矛盾    自己写一下就知道了 只有被整出才会成立。

每个定义域的循环节都可以与每个值域的循环节匹配(满足红字匹配成功)

那么就是找两个序列的不相交的循环节,对于定义域里面的每一个循环节都找出来有多少种情况。每一个循环节的情况数为它所能匹配的值域循环节的长度 和。每个循环节的情况数起来就是答案了

样例一  (1+1)*(1+1)=4; a两个环(长度 2 1)           b两个个环(长度 1 1)       2与1 1匹配   1与1 1 匹配

样例二  (1+3)=4;           a一个环(长度 3)                b两个环(长度 1 3)         3与1 3匹配 

注   对于一个a循环节  若b循环节中没有能与之匹配的   答案为零   想想为什么  不用特判累积过程中可以处理掉

AC代码

 1 #include <stdio.h>
 2 #include <math.h>
 3 #include <string.h>
 4 #include <stdlib.h>
 5 #include <iostream>
 6 #include <sstream>
 7 #include <algorithm>
 8 #include <string>
 9 #include <queue>
10 #include <vector>
11 using namespace std;
12 const int maxn= 1e5+10;
13 const int mod= 1e9+7;
14 const int inf = 0x3f3f3f3f;
15 typedef long long ll;
16 int n,m;
17 int a[maxn],b[maxn];
18 int vis[maxn],loop_a[maxn],loop_b[maxn];   //loop存循环节长度
19 int main()
20 {
21     int kase=0;
22     while(scanf("%d %d",&n,&m)!=EOF)
23     {
24         for(int i=0; i<n; i++)
25             scanf("%d",&a[i]);
26         for(int i=0; i<m; i++)
27             scanf("%d",&b[i]);
28         memset(vis, 0, sizeof(vis));      //标记是否访问过 找不相交的循环节
29         memset(loop_b, 0, sizeof(loop_b));
30         memset(loop_a, 0, sizeof(loop_a));
31         int cnt1=0,cnt2=0;                   //cnt记录循环节个数
32         for(int i = 0; i < n; i++)
33         {
34             if(!vis[i])                      //过程自己模拟一下就明了了
35             {
36                 int x = a[i];
37                 int len = 0;
38                 while(!vis[x])
39                 {
40                     len++;
41                     vis[x] = 1;
42                     x = a[x];
43                 }
44                 if(len!=0)
45                     loop_a[cnt1++]=len;         //保存循环节
46             }
47         }
48         memset(vis, 0, sizeof vis);
49         for(int i = 0; i < m; i++)      //b同理
50         {
51             if(!vis[i])
52             {
53                 int x = b[i];
54                 int len = 0;
55                 while(!vis[x])
56                 {
57                     len++;
58                     vis[x] = 1;
59                     x = b[x];
60                 }
61                 if(len!=0)
62                     loop_b[cnt2++]=len;
63             }
64         }
65         ll ans=1;
66         for(int i=0; i<cnt1; i++)            //枚举a与b的循环节 试了不会超时 还有更快一些的做法
67         {
68             ll sum=0;                //记录每个循环节的情况数
69             for(int j=0; j<cnt2; j++)    
70             {
71                 if(loop_a[i]%loop_b[j]==0)    //只有是约数才能匹配
72                 {
73                     sum=(sum+loop_b[j])%mod;     //加进去 取模
74                 }
75             }
76             ans=(ans*sum)%mod;                //乘到答案里面
77         }
78         printf("Case #%d: %lld\n",++kase,ans);
79     }
80     return 0;
81 }

作者水平有限 欢迎大佬纠错!

多校真jb难啊~~~~~QAQ

太菜了

posted @ 2017-11-03 00:31  灬从此以后灬  阅读(200)  评论(0编辑  收藏  举报