题意:给你一个序列a,是0到n-1的一个排列,给你一个序列b,是0到m-1的一个排列,问你有多少种不同的函数关系满足:f[i] = b[f[a[i]]] (0 < i < n).
官方题解:考虑置换 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 的循环节的个数。
时间复杂度是 O(n + m) 。
官方的题解有些乱码。。。。可以参考帅神的博客:点击打开链接
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 1e6+5;
int a[maxn], b[maxn];
int n, m;
bool visA[maxn], visB[maxn];
int numA[maxn], numB[maxn];
int main(void)
{
int ca = 1;
while(cin >> n >> m)
{
memset(numA, 0, sizeof(numA));
memset(numB, 0, sizeof(numB));
memset(visA, 0, sizeof(visA));
memset(visB, 0, sizeof(visB));
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
for(int i = 0; i < m; i++)
scanf("%d", &b[i]);
int cntA = 0, cntB = 0;
for(int i = 0; i < n; i++)
{
int len = 0;
int tmp = i;
while(!visA[tmp])
{
len++;
visA[tmp] = 1;
tmp = a[tmp];
}
if(len)
numA[cntA++] = len;
}
for(int i = 0; i < m; i++)
{
int len = 0;
int tmp = i;
while(!visB[tmp])
{
len++;
visB[tmp] = 1;
tmp = b[tmp];
}
if(len) numB[len]++;
}
ll ans = 1;
for(int i = 0; i < cntA; i++)
{
ll tmp = 0;
for(int j = 1; j*j <= numA[i]; j++)
{
if(numA[i]%j == 0)
{
if(j*j == numA[i])
tmp += numB[j]*j;
else
tmp += numB[j]*j+numB[numA[i]/j]*numA[i]/j;
}
}
ans = ans*tmp%mod;
}
printf("Case #%d: %lld\n", ca++, ans);
}
return 0;
}
本文解析了一道关于函数关系计数的问题,给出了详细的官方题解,并通过分析置换的循环节来解决该问题。最终提供了O(n+m)时间复杂度的代码实现。
447

被折叠的 条评论
为什么被折叠?



