2017 Multi-University Training Contest

来源:互联网 发布:苹果mac 怎样更新系统 编辑:程序博客网 时间:2024/06/05 04:06

Function

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1354    Accepted Submission(s): 624


Problem Description
You are given a permutation a from 0 to n1 and a permutation b from 0 to m1.

Define that the domain of function
f is the set of integers from 0 to n1, and the range of it is the set of integers from 0 to m1.

Please calculate the quantity of different functions
f satisfying that f(i)=bf(ai) for each i from 0 to n1.

Two functions are different if and only if there exists at least one integer from
0 to n1 mapped into different integers in these two functions.

The answer may be too large, so please output it in modulo
109+7.
 

Input
The input contains multiple test cases.

For each case:

The first line contains two numbers n,m.(1n100000,1m100000)

The second line contains
n numbers, ranged from 0 to n1, the i-th number of which represents ai1.

The third line contains
m numbers, ranged from 0 to m1, the i-th number of which represents bi1.

It is guaranteed that
n106,m106.
 

Output
For each test case, output "Case #x:y" in one line (without quotes), where x indicates the case number starting from 1 and y denotes the answer of corresponding case.
 

Sample Input
3 21 0 20 13 42 0 10 2 3 1
 

Sample Output
Case #1: 4Case #2: 4

 

题目说函数f,它的定义域取自数组b,然后看是否能够满足对应的关系使得 f(i) = b(fa[i])

说一下第一组测试数据是怎么来的:

按照上面树的顺序:

取   f(0)=0   , f(1) = 0,   f(2) = 0;

bf(a[0]) = bf(1) = 0 = f(0) 成立

bf(a[1]) = bf(0) = 0 = f(1) 成立

bf(a[2]) = bf(2) = 0 = f(2) 成立

则 0 0 0 就是一组符合要求的解。按照上面的方法可以找到其他的解:

分别是  0  0  1

             1  1  0

             1  1  1

所以答案是4:


我们还可以分析题目中给出的第二组测试数据:

f(0) = bf(a[0]) = bf(2)

f(1) = bf(a[1]) = bf(0)

f(2) = bf(a[2]) = bf(1)


从式子中可以看出,如果现在从b数组中取一个值给f(0),则就可以推出f(1),当推出f(1)后,就可以推出f(2),

如果此时f(2)的值代进第一个式子,能够推得f(0),说明最初对f(1)的假设是正确的,否则这组解就是错误的。


因此假如现在已经知道f(i)的值可以满足一个循环节。

知道f(i) 就能知道  f(a[i]) ,知道f(a[i]),就能知道f(aa[i]),然后就能知道f(aaa[i]),因此一直这样下去肯定会再

回到f(i)。

f(i) -> f(a[i]) -> f(aa[i]) -> f(aaa[i]) - >  ...... f(aa...aa[i]) = f(i) ,这样构成一个环,

则找数组a中的环的方法就是,确定  i ,a[i]  ,aa[i]往前翻找,直到找到环了为止,假设环的长度为len.

即 i ,a[i] ,aa[i] ,aaa[i] ,  ...... ,aa...aa[i] ,   最后aa,,,aa[i] = i,最后a有len个,则环的长度就是len.


对于一个len长度循环节中的f(i),我们有如下推导

f(i) = bf(a[i])

因为f(a[i]) = bf(a[a[i]])    简便记录:变成 bf(aa[i])

带入上式

f(i) = bf(a[i]) = bbf(aa[i]) 

同样的道理往下推导:

f(i) = bf(a[i]) = bbf(aa[i])  = bbbf(aaa[i]) = bb...bbf(aa..aa[i])

因为f(i)所在的环的循环节的长度为len,则经过len个aa,,aa[i]会回到  i ,则最后  f(aa...aa[i]) = f(i),

同样最后b的长度也是len,我们知道f(i)无非就是b中的一个数,假如这个数是num

则bb,,,bbf(i)倒着推导回去       num,b[num],bb[num],....,bb..bb[num]

可以看出b数组中也对应着一些环,对于b,只要其循环节的长度是len的因子的话,这个循环节

中的所有数字,都能够打通这个长度为len的a中的环。不信的话,可以举例子证明这个结论:

假如a中一个环的长度为6.

f(i) = bbbbbbf(i)   6的因子为1,2,3,6

(1)假如b循环节的长度为1,其值为x

f(i) = x;

从后往前推导:

.,x,x,x,x,x,x,x

则b环中的一个元素x可以打通长度为6的a环。

(2)假如b循环节的长度为2,其值为x,y,x,y,x,y........

f(i) = x

往前推

x,y,x,y,x,y,x      元素x成功打通长度为6的a环

f(i) = y 从后往前推导

y,x,y,x,y,x,y      元素y成功打通长度为6的a环

则,b循环节长度为2,循环元素为x,y。这两个元素都能打通长度为6的a环。方案数就是2种

(3)假如b循环节长度为3,其值为x,y,z,循环是x->y->z->x

f(i)  = x;

.x,z,y,x,z,y,x     元素x成功打通长度为6的a环

 f(i) = y

 y,x,z,y,x,z,y      元素y成功打通长度为6的a环

 f(i)  = z       

 z,y,x,z,y,x,z      元素z成功打通长度为6的a环。

则b循环节中的循环元素x,y,z都能打通长度为6的a环,那么方案数就是3种

(4)b循环节长度为6的情况,按上面方法推导吧。好累,就不推了,到这一步应该就明白了。

从上可以看出,b环种的数都是我设的,就用未知数表示,推理一下过程,可以看出,根a环和b环中的具体数字是没有

关系的,仅仅与他们的长度有关系。

因此才得到这样的结论:如果a中有长度为lena的循环节,b中存在lenb长度的循环节,如果lena%lenb==0

,则lenb中的所有元素的个数就是lenb,这些元素都能打通a环,a环,其方案数就是lenb种。

因此题目关键就是找环了。


下面是AC代码:


#include <iostream>#include <stdio.h>#include <string.h>#include <vector>using namespace std;typedef long long LL;const int maxn = 100010;const int mod = 1e9+7;int a[maxn];     ///放置序列aint b[maxn];     ///放置序列bint A[maxn];     ///A[i]代表序列a中循环节长度为i的环出没出现过int B[maxn];     ///B[i]代表序列b中循环节长度为i的环出没出现过int visa[maxn];  ///标记序列a中已经成环的那些数int visb[maxn];  ///标记序列b中已经成环的那些数int n,m;int main(){    int Case=0;    while(~scanf("%d%d",&n,&m))    {        memset(visa,0,sizeof(visa));        memset(visb,0,sizeof(visb));        memset(A,0,sizeof(A));        memset(B,0,sizeof(B));        vector<int>va;   ///将序列a中各个环的长度,存放到容器中,每个长度只放置一次        vector<int>vb;   ///将序列b中各个环的长度,存放到容器中,每个长度只放置一次        for(int i = 0; i < n; i++)            scanf("%d",&a[i]);        for(int j = 0; j < m; j++)            scanf("%d",&b[j]);        for(int i = 0; i < n; i++)        {            if(!visa[i])  ///i这个位置上的数不在任何环总            {                int pos = i;                int len = 0;                while(!visa[pos])                {                    len++;                    visa[pos] = 1;                    pos = a[pos];                }                if(A[len]==0)                    va.push_back(len);                A[len]++;            }        }        for(int i = 0; i < m; i++)        {            if(!visb[i])            {                int pos = i;                int len = 0;                while(!visb[pos])                {                    len++;                    visb[pos] = 1;                    pos = b[pos];                }                if(B[len]==0)                    vb.push_back(len);                B[len]++;            }        }        long long ans = 1;        for(int i = 0; i < va.size(); i++)        {            int lena = va[i];            int temp = 0;            for(int j = 0; j < vb.size(); j++)            {               int lenb = vb[j];               if(lena%lenb==0)               {                   temp = (temp + (LL)(lenb*B[lenb]))%mod;               }            }            for(int k = 1; k <= A[lena]; k++)                ans = (ans*temp)%mod;        }        printf("Case #%d: %lld\n",++Case,ans);    }    return 0;}


原创粉丝点击