Function
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1354 Accepted Submission(s): 624
Define that the domain of function f is the set of integers from 0 to n−1 , and the range of it is the set of integers from 0 to m−1 .
Please calculate the quantity of different functions f satisfying that f(i)=bf(ai) for each i from 0 to n−1 .
Two functions are different if and only if there exists at least one integer from 0 to n−1 mapped into different integers in these two functions.
The answer may be too large, so please output it in modulo 109+7 .
For each case:
The first line contains two numbers n, m . (1≤n≤100000,1≤m≤100000)
The second line contains n numbers, ranged from 0 to n−1 , the i -th number of which represents ai−1 .
The third line contains m numbers, ranged from 0 to m−1 , the i -th number of which represents bi−1 .
It is guaranteed that ∑n≤106, ∑m≤106 .
3 2 1 0 2 0 1 3 4 2 0 1 0 2 3 1
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;
}