【Link】:http://codeforces.com/contest/834/problem/C
【Description】
给你两个排列a和b;
a排列的长度为n,b排列的长度为m;
a∈[0..n-1],b∈[0..m-1];
然后让你求一个函数f[i];
f[i]的定义域为0..n-1,值域为0..m-1
同时使得对于任意f[i],i∈[0..n-1];
f(i)=bf(a[i])成立;
【Solution】
原始可以递推一下;
f(i)=bf(ai)=bbf(aai)
则可以一直写下去f[i]=bbbbbf(aaaaa[i]);
注意到a是一个排列;
最后肯定能形成一个环,则aaaaa..a[i]肯定又能变回i
则
f(i)=b⋯bf(i)l times b
(这里L是第一次回到i的L);
这里的含义其实就相当于f[i]是一个x
要使得
x=b....bx
而b也是一个排列;
则也肯定有循环节;
这里从x开始的b数组的循环节长度一定得是上面的a的循环节的长度L的因子;
不然就不能在L次b之后回到x了;
于是,
在a数组里找循环节的长度,在b数组中也找循环节的长度;
看看有多少个长度在a中有,且b数组中,有它的因子长度的循环节;
直接累加因子循环节长度到temp中;
然后累乘所有temp即可;
根据上面的形式,每个a循环节中的某一个位置,f只要确定了,其他该循环节中的f值也就确定了,然后那个位置有temp种选择;就是因子循环节中任意一个b[i]都可以;
找因子的时候,需要做些优化;
不然可能退成O(n2)的复杂度;
先枚举a数组有哪些循环节,长度为i;
然后用O(i12)复杂度枚举它可能的因子,(j是则n/j也是)
看看在b中有没有这样长度的;
【NumberOf WA】
0
【Reviw】
求因子的思想很好.
【Code】
#include <bits/stdc++.h>
using namespace std;
#define int long longconst int N = 1e5;
const int MOD = 1e9+7;int n,m;
int a[N+10],b[N+10],cnta[N+10],cntb[N+10];
bool flag[N+10];main(){int kk = 0;while (~scanf("%lld%lld",&n,&m)){for (int i = 1;i <= n;i++){scanf("%lld",&a[i]);a[i]++;}for (int i = 1;i <= m;i++){scanf("%lld",&b[i]);b[i]++;}memset(cnta,0,sizeof cnta);memset(cntb,0,sizeof cntb);memset(flag,0,sizeof flag);for (int i = 1;i <= m;i++)if (!flag[i]){int x = i,num = 0;while (!flag[x]){flag[x] = 1;num++;x = b[x];}cntb[num]++;}memset(flag,0,sizeof flag);for (int i = 1;i <= n;i++)if (!flag[i]){int x = i,num = 0;while (!flag[x]){flag[x] = 1;num++;x = a[x];}cnta[num]++;}int ans = 1;for (int i = 1;i <= n;i++)if (cnta[i]>0){int temp = 0;for (int j = 1;j*j <= i;j++)if (i%j==0){temp = (temp + j*cntb[j])%MOD;if (j != i/j)temp = (temp + (i/j)*cntb[i/j])%MOD;}while (cnta[i]--){ans = (ans*temp)%MOD;}}printf("Case #%lld: %lld\n",++kk,ans);}return 0;
}