2017 Multi-University Training Contest - Team 1 :Function

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 2 1 0 2 0 1 3 4 2 0 1 0 2 3 1
 

Sample Output
  
  
Case #1: 4 Case #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];     ///放置序列a
int b[maxn];     ///放置序列b
int 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值