poj 3128 Leonardo's Notebook(置換べき乗)


http://poj.org/problem?id=3128
26文字の大文字を含む文字列を入力すると、置換と見なして、置換が置換の平方であるかどうかを判断することができます.
構想:詳細は置換群の高速べき乗演算研究と検討を参考にすることができる.
まず、置換された平方に何が起こっているのかを考えてみましょう.置換中のループについては,その長さが偶数であれば,平方以降は必ず2つの長さが等しいループに分けられ,長さが奇数であれば平方以降も1つのループであり,長さは変わらない.したがって、現在の置換を考慮すると、あるサイクルの長さが偶数であれば、元の置換平方で得られたものに違いなく、等長のサイクルには必ず偶数個がある.奇数の長さのサイクルについては、偶数の長さのサイクル二乗を元に置き換えて得られるか、奇数の長さのサイクル二乗で得られるかのいずれかである.従って、現在置換がある置換であるか否かを判定する二乗は、現在置換長が偶数であるか否かを判定するサイクル個数が偶数であるか否かに等しい.
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#define LL long long
#define _LL __int64
#define eps 1e-8
#define PI acos(-1.0)
using namespace std;

const int maxn = 1010;
const int INF = 0x3f3f3f3f;

int a[30];
char s[30];
int vis[30];
int num[30];

bool solve()
{
	memset(vis,0,sizeof(vis));
	memset(num,0,sizeof(num));
	int i;

	while(1)
	{
		for(i = 1; i <= 26; i++)
			if(!vis[a[i]])
				break;
		if(i > 26)
			break;
		int cnt = 1;
		vis[i] = 1;  //      i
		int t = a[i];
		vis[t] = 1;
		while(t != i)
		{
			vis[t] = 1;
			t = a[t];
			cnt++;
		}
		if(cnt % 2 == 0) //   ,        1
			num[cnt]++;
	}
	int flag = 1;
	for(int i = 2; i <= 26; i += 2)
	{
		if(num[i] % 2 != 0)
		{
			flag = 0;
			break;
		}
	}
	if(flag == 1)
		return true;
	return false;
}

int main()
{
	int test;
	scanf("%d",&test);
	while(test--)
	{
		scanf("%s",s+1);
		for(int i = 1; i <= 26; i++)
			a[i] = s[i]-'A' + 1;

		if(solve())
			printf("Yes
"); else printf("No
"); } return 0; }