如果不知道抽象代数(近世代数)中的排列分解这个知识点,建议去学习一下再来看这道题目。
直接看第二个样例吧,按照题目的意思可以得到如下方程。
然后进一步可得
函数有多少种,其实也就是上面这个方程有多少组解。
现在来观察一下这个方程,多么漂亮的形式,是不是很类似抽象代数中的循环排列呢?
不就说明了x是一个3阶循环排列中的元素吗!
不信的话,来看下面这个3阶循环排列
我们把f(0) = 2或者f(0) = 3或者f(0) = 1带入第一个方程发现都是成立的,同理第二和第三个方程也是这样,带入2 3 1也都是成立的。
但是实际上3个方程之间会相互制约,答案并不是9种,实际上只有3组解。
这三组解是循环的。
再来看这样一个1阶循环排列
虽然这个排列的阶数小于元素的阶数,但是0仍然是上面那个方程的解,读者可以自行验证。
也就是说如果是一个n阶循环排列,
是一个m阶循环排列,如果m
| n,则在n到m之间能够建立m个满足题意的映射。
所以我们需要将排列a和排列b分解成一个个的循环排列,然后暴力搜索一遍即可。
代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdlib>
#include <vector>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAX_N = 100005;
void f(vector<int>& v, int a[], int n)
{
int mark[MAX_N] = {0};
for (int i = 0; i < n; i++)
{
if (!mark[i])
{
mark[i] = 1;
int t = a[i];
int cnt = 1;
while (t != i)
{
mark[t] = 1;
t = a[t];
cnt++;
}
v.push_back(cnt);
}
}
}
int main()
{
//freopen("test.txt", "r", stdin);
int n, m;
int Case = 1;
int a[MAX_N], b[MAX_N];
while (~scanf("%d%d", &n, &m))
{
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
for (int i = 0; i < m; i++)
scanf("%d", &b[i]);
vector<int> v1;
f(v1, a, n);
vector<int> v2;
f(v2, b, m);
int ans = 1;
for (int i = 0; i < v1.size(); i++)
{
int tmp = 0;
for (int j = 0; j < v2.size(); j++)
{
if (v1[i] % v2[j] == 0)
tmp += v2[j];
}
ans *= tmp;
}
printf("Case #%d: ", Case++);
cout << ans << endl;
}
return 0;
}