Function
终于把这道题搞会了,有个地方真的很难理解。
考虑置换
a
的一个循环节,长度为
l
,那么有 $f(i) = b_{f(a_i)} = b_{b_{f(a_{a_i})}} = \underbrace{b_{\cdots b_{f(i)}}}_{l\text{ times }b}$ 。
那么 f(i) 的值在置换 b 中所在的循环节的长度必须为 l 的因数。
而如果 f(i) 的值确定下来了,这个循环节的另外 l−1 个数的函数值也都确定下来了。
答案就是 ∑i=1k∑j∣lij⋅calj 改为 ∏i=1k∑j∣lij⋅calj ,其中 k 是置换 a 中循环节的个数, li 表示置换 a 中第 i 个循环节的长度, calj 表示置换 b 中长度为 j 的循环节的个数。
时间复杂度是 $\mathcal{O}(n + m)$ 。
一直不明白为什么a中循环节的长度为b中循环节的长度的倍数才符合条件。这个函数确实很难理解,当时和队友讨论的时候把两个数组合在一起讨论循环节,却忽略了单个数组里面也存在循环节,而且还符合某些性质。做多校的时候大家都被1002坑住了,到后面没什么时间写这个题,对比一下才发现差距好大,做比赛跟休闲似的。。。。
循环节直接dfs或者暴力就可以预处理出来。
const int N=1e5+10;
int n,m,a[N],b[N],vis[N];
ll numa[N],numb[N],la[N];
void init()
{
memset(la,0,sizeof(la));
memset(vis,0,sizeof(vis));
memset(numa,0,sizeof(numa));
memset(numb,0,sizeof(numb));
}
int main()
{
int t=1;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=0; i<n; i++) scanf("%d",&a[i]);
for(int i=0; i<m; i++) scanf("%d",&b[i]);
ll ans=1;
int ma1=0,ma2=0,cnt=0;
for(int i=0; i<m; i++)
if(!vis[b[i]])
{
int num=1,pre=b[b[i]];
vis[b[i]]=1;
while(!vis[pre])
{
num++;
vis[pre]=1;
pre=b[pre];
}
numb[num]++;//长度为num的个数
ma2=max(num,ma2);
}
memset(vis,0,sizeof(vis));
for(int i=0; i<n; i++)
if(!vis[a[i]])
{
int num=1,pre=a[a[i]];
vis[a[i]]=1;
while(!vis[pre])
{
num++;
vis[pre]=1;
pre=a[pre];
}
numa[cnt++]=num;//长度为num是否出现过
la[num]=-1;
ma1=max(num,ma1);
}
for(int i=1; i<=ma2; i++)
if(numb[i])
{
ll tmp=numb[i],id=i;
for(int j=i; j<=ma1; j+=i)
if(la[j])
{
if(la[j]==-1) la[j]=0;
la[j]+=id*tmp;
}
}
for(int i=1; i<=ma1; i++) if(la[i]<0) la[i]=0;
for(int i=0; i<cnt; i++)
ans=ans*la[numa[i]]%MOD;
printf("Case #%d: %lld\n",t++,ans);
}
return 0;
}
/*
3 3
0 1 2
0 1 2
3 2
0 1 2
1 0
*********
27
0
*********
*/