POJ3128 Leonardo's Notebook【置换群】

题目链接:

http://poj.org/problem?id=3128


题目大意:

给你一行共 26 个字母,代表一个置换。问:这个置换能否为某个置换平方的结果。


解题思路:

这道题可参考《置换群快速幂运算研究与探讨》,里边有详解。这里放上结论。

结论一: 一个长度为 l 的循环 T,l 是 k 的倍数,则 T^k 是 k 个循环的乘积,每个

循环分别是循环 T 中下标 i mod k=0,1,2… 的元素按顺序的连接。 
结论二:一个长度为 l 的循环 T,gcd(l,k)=1,则 T^k 是一个循环,与循环 T 不一

相同。

结论三:一个长度为 l 的循环 T,T^k 是 gcd(l,k)个循环的乘积,每个循环分别是循

 T 中下标 i mod gcd(l,k)=0,1,2… 的元素的连接。 

这倒题中,应用上边的结论来判断这个置换是否为某个置换平方的结果。

一个置换平方可得到:循环节为奇数的置换的平方仍为奇数项的置换,循环节为偶数

的置换的平方分裂成了两个循环节相同的置换。

那么当前置换是由什么置换而来的:

对于奇数项的置换,有可能是由奇数项的置换平方得到的,也有可能是有偶数项的置

换平方得到的。对于偶数项的置换,它一定是原始置换为偶数项的置换分裂得到的。

而且当前置换中偶数项的置换一定成对出现。

那么问题就变为了:对于偶数项的置换,是否成对出现。

那么,我们现在查找 26 个字母的所有置换,计算出每个置换的循环节长度。统计循

环节长度的个数。遍历查找偶数项的置换数目,如果出现奇数,则输出"No",否则

输出"Yes"。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;

char s[33];
bool vis[33];
int ss[33],Cnt[33];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(s,0,sizeof(s));
        memset(vis,false,sizeof(vis));
        memset(Cnt,0,sizeof(Cnt));
        scanf("%s",s);
        for(int i = 0; i < 26; ++i)
            ss[i] = s[i] - 'A';
        int flag = 1;
        for(int i = 0; i < 26; ++i)
        {
            if(vis[i] == 0)
            {
                vis[i] = 1;
                int temp = ss[i];
                int num = 1;
                while(temp != i)
                {
                    vis[temp] = 1;
                    temp = ss[temp];
                    num++;
                }
                Cnt[num]++;
            }
        }

        for(int i = 2; i < 26; i+=2)
            if(Cnt[i]%2)
            {
                flag = 0;
                break;
            }

        if(flag)
            printf("Yes\n");
        else
            printf("No\n");

    }

    return 0;
}


猜你喜欢

转载自blog.csdn.net/u011676797/article/details/49474067