POJ 2369|URAL 1024|Permutations|置换求循环节长度

来源:互联网 发布:数据库查询怎么保存 编辑:程序博客网 时间:2024/06/07 19:55

题目

我们注意到一些不可变集合(?)的置换是一个一对一的自我映射。更通俗地说,置换是重新排序集合的一种方法。举个例子:我们定义{1,2,3,4,5}的置换为:
P: {1,2,3,4,5}->{4,1,5,2,3}
也就是说,我们定义置换P为:P(1)=4,P(2)=1,P(3)=5, etc.
那么P(P(1))的值为多少呢?显然P(P(1))=P(4)=2,P(P(3))=P(5)=3。所以如果P(n)是一个置换,那么P(P(n))也是一个置换:
P(P(n)): {1,2,3,4,5}->{2,4,3,1,5}
自然,我们定义P2(n)=P(P(n))。一般地,我们定义P(n)=P1(n),Pk(n)=P(k1)(n)
置换中有一个很重要的置换:
EN(n): {1,2,3,...,N} -> {1,2,3,...,N}
显然EN满足EkN=EN
下面有一个定理:令P(n)是一个置换,那么存在一个最小的整数k,使得Pk=EN,求出这个k。

输入

输入的第一行一个正整数N(1N1000)
接下来一行N个整数P(i)

输出

一行一个正整数k,你可以认为k109

题解

如果Pk=EN,那么显然有P(k+1)=P,那么我们就把本题转化为求置换的循环节长度了,循环节长度就等于k。求循环节长度通过求置换内的环的长度再求最小公倍数即可。

#include <cstdio>#define FOR(i,j,k) for(i=j;i<=k;++i)const int N = 1005;int gcd(int a, int b) {    return b == 0 ? a : gcd(b, a % b);}int lcm(int a, int b) {    return a / gcd(a, b) * b;}int p[N], vis[N];int main() {    int n, ans = 1, i;    scanf("%d", &n);    FOR(i,1,n) scanf("%d", &p[i]);    FOR(i,1,n) if (!vis[i]) {        int x = 0;        for (int j = i; !vis[j]; j = p[j]) {            vis[j] = 1;            ++x;        }        ans = lcm(ans, x);    }    printf("%d\n", ans);    return 0;}