HDU-5392-Infoplane in Tina Town
这道题是lcm。只不过是多个数的lcm。
这个题目大意我硬是理解了好久,就很卡。。
题目大意:题目背景是一个游戏,说是你站在当前格子上会给你下一步走的位置。那么转换为我们的抽象含义:给出一组序列。让你恢复初始序列。(即让给定序列的数字恢复到他应该在的位置,比如数字1应该在1的位置,数字5应该在5的位置。)
要分解循环长度。然后找到这些长度的最小公倍数。
我们这里找最小公倍数的方法是分解质因数的方法。
那么什么是分解循环长度?
比如样例:
长度:6
下标:1 2 3 4 5 6
元素:2 1 4 5 6 3
比如我们现在站在下标为1这个格子上,a[1] = 2;给出了下一步走的位置:是2这个格子。所以我们来到了2这个格子。。。然后依次下去走下去。直到走完本次循环。
比如我们的一个循环长度(下面列出的是走的下标):
1 -> 2 -> 1 -> 2-> 1 -> 2…
这就是一个循环。这个循环的长度是2;
3 -> 4 -> 5 -> 6 -> 3 -> 4 -> 5 -> 6…
这也是一个循环。循环长度是4;
我们找到循环长度的最小公倍数是4;
方法:分解质因数;
求解多个数的最小公倍数的方法:
把这些数分解质因数,然后找到公共的质因数,用公共质因数的最高次幂乘以每个数的其他质因数就是这些数的最小公倍数
那么什么是公共质因数的最高次幂。就是这些数的共有的质因数,然后能够除以最多次的那个质因数的次数,就是公共质因数的最高次幂。
比如我们求解14, 16的最小公倍数。
14 = 2 * 7;(2出现了1次,7出现了1次)
16 = 2 * 2 * 2 * 2;(2出现了4次)
公共质因数为:2;
最高次幂为:4;
就是出现次数最多的。
这两部分解决完之后就很好解决了。
num[]数组:下标存储的是因数。里面存的值为出现最多的次数。
我们把那些下标为合数的值赋值为1.(在后面维护答案的时候乘以1是不发生改变的)
cnt保存的是每次求得循环长度。
代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e6 + 10;
const ll mod = 3221225473;
int t;
int a[N];
int vis[N];
int num[N];
int n;
int main()
{
cin >> t;
while (t--)
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf ("%d", &a[i]);
vis[i] = 0;
num[i] = 0;
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
int cnt = 0;
int k = i;
while (!vis[k])//这部分求的是循环长度
{
vis[k] = 1;
cnt++;
k = a[k];
}
for (int j = 2; j * j <= cnt; j++)
{
int top = 0;
if (!(cnt % j))//这里求的是质因数
{
while (!(cnt % j))
{
cnt /= j;
top++;
}
}
num[j] = max(num[j], top);//维护最高次幂的质因数
}
if (cnt > 1)//这里就是上面说的方便后面累乘
{
num[cnt] = max(num[cnt], 1);
}
}
}
ll res = 1;
for (int i = 2; i <= n; i++)
{
while (num[i]--)
{
res = res * i % mod;
}
}
cout << res << endl;
}
return 0;
}