poj1026 & poj1721 & poj3128 & poj3590(置换)

Cipher
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 22855 Accepted: 6357

Description

Bob and Alice started to use a brand-new encoding scheme. Surprisingly it is not a Public Key Cryptosystem, but their encoding and decoding is based on secret keys. They chose the secret key at their last meeting in Philadelphia on February 16th, 1996. They chose as a secret key a sequence of n distinct integers, a1 ; . . .; an, greater than zero and less or equal to n. The encoding is based on the following principle. The message is written down below the key, so that characters in the message and numbers in the key are correspondingly aligned. Character in the message at the position i is written in the encoded message at the position ai, where ai is the corresponding number in the key. And then the encoded message is encoded in the same way. This process is repeated k times. After kth encoding they exchange their message. 

The length of the message is always less or equal than n. If the message is shorter than n, then spaces are added to the end of the message to get the message with the length n. 

Help Alice and Bob and write program which reads the key and then a sequence of pairs consisting of k and message to be encoded k times and produces a list of encoded messages. 

Input

The input file consists of several blocks. Each block has a number 0 < n <= 200 in the first line. The next line contains a sequence of n numbers pairwise distinct and each greater than zero and less or equal than n. Next lines contain integer number k and one message of ascii characters separated by one space. The lines are ended with eol, this eol does not belong to the message. The block ends with the separate line with the number 0. After the last block there is in separate line the number 0.

Output

Output is divided into blocks corresponding to the input blocks. Each block contains the encoded input messages in the same order as in input file. Each encoded message in the output file has the lenght n. After each block there is one empty line.

Sample Input

10
4 5 3 7 2 8 1 6 10 9
1 Hello Bob
1995 CERC
0
0

Sample Output

BolHeol  b
C RCE


题意:给定一个n个元素的置换,然后输入置换的操作次数k后再输入一个字符串,要求对字符串进行k次置换后的结果字符串输出。

思路:寻找置换中的循环节,对于这个循环节来书,置换的次数就等于k模循环节中元素个数。


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int pl[205];
bool vis[205];
int cmp[205];
int num[205];
int start[205];
int ans[205];
int cnt;
void dfs(int i)  //循环群编号 
{
	if(vis[i])return;
	vis[i]=true;
	cmp[i]=cnt;
	num[cnt]++;
	dfs(pl[i]);
}
void dfs2(int x,int t)  //寻找循环群中代表元素的去向 
{
	int tmp=x;
	for(int i=0;i<t;i++)
	{
		tmp=pl[tmp];
	}
	ans[x]=tmp;
}
int main()
{
	int n;
	int k;
	char s[205];
	char output[205];
	while(~scanf("%d",&n)&&n)
	{
		cnt=0;
		memset(vis,0,sizeof(vis));
		memset(num,0,sizeof(num));
		memset(start,-1,sizeof(start));
		for(int i=0;i<n;i++)  //一次操作的去向 
		{
			scanf("%d",&pl[i]);
			pl[i]--;
		}
		for(int i=0;i<n;i++)  //循环节编号 
		{
			if(!vis[i])
			{
				start[cnt]=i;
				dfs(i);
				cnt++;
			}
		}
		while(~scanf("%d",&k)&&k)
		{
			for(int i=0;i<cnt;i++)
			{
				int t=k%num[i];
				dfs2(start[i],t);
				int p=start[i],q;
				for(q=pl[start[i]];q!=start[i];q=pl[q])  //同一循环群中后一个去向为前一个去向的后一个 
				{
					ans[q]=pl[ans[p]];
					p=pl[p];
				}
			}
			getchar();
			scanf("%[^\n]",s);
			if(strlen(s)<n)
			{
				for(int i=strlen(s);i<n;i++)
				{
					s[i]=' ';
				}
				s[n]='\0';
			}
			for(int i=0;i<n;i++)output[ans[i]]=s[i];
			output[n]='\0';
			printf("%s\n",output);
		}
		printf("\n");
	}
}

CARDS
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 2048 Accepted: 1058

Description

Alice and Bob have a set of N cards labelled with numbers 1 ... N (so that no two cards have the same label) and a shuffle machine. We assume that N is an odd integer. 
The shuffle machine accepts the set of cards arranged in an arbitrary order and performs the following operation of double shuffle : for all positions i, 1 <= i <= N, if the card at the position i is j and the card at the position j is k, then after the completion of the operation of double shuffle, position i will hold the card k. 

Alice and Bob play a game. Alice first writes down all the numbers from 1 to N in some random order: a1, a2, ..., aN. Then she arranges the cards so that the position ai holds the card numbered a i+1, for every 1 <= i <= N-1, while the position aN holds the card numbered a1. 

This way, cards are put in some order x1, x2, ..., xN, where xi is the card at the i th position. 

Now she sequentially performs S double shuffles using the shuffle machine described above. After that, the cards are arranged in some final order p1, p2, ..., pN which Alice reveals to Bob, together with the number S. Bob's task is to guess the order x1, x2, ..., xN in which Alice originally put the cards just before giving them to the shuffle machine. 

Input

The first line of the input contains two integers separated by a single blank character : the odd integer N, 1 <= N <= 1000, the number of cards, and the integer S, 1 <= S <= 1000, the number of double shuffle operations. 
The following N lines describe the final order of cards after all the double shuffles have been performed such that for each i, 1 <= i <= N, the (i+1) st line of the input file contains pi (the card at the position i after all double shuffles). 

Output

The output should contain N lines which describe the order of cards just before they were given to the shuffle machine. 
For each i, 1 <= i <= N, the ith line of the output file should contain xi (the card at the position i before the double shuffles). 

Sample Input

7 4
6
3
1
2
4
7
5

Sample Output

4
7
5
6
1
2
3

题目大意:
第i个位置的牌是a[i],一次交换后第i个位置的牌变成a[a[i]]。序列所有位置经过一次交换为一次交换,已知交换m次之后的序列,求原先序列。

思路:

先用模拟的方法求出循环节t,再通过t - s % t次交换即可得到原序列。//t为循环节,s为给出的变换次数

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int pl[1005];
int tmp[1005];
int p[1005];
int ans[205];
int cnt;
int main()
{
	int n,s;
	while(~scanf("%d%d",&n,&s))
	{
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&pl[i]);
			tmp[i]=pl[i];
		}
		cnt=0;
		while(true)
		{
			cnt++;
			for(int i=1;i<=n;i++)
			{
				p[i]=tmp[tmp[i]];
			}
			bool flag=true;
			for(int i=1;i<=n;i++)
			{
				if(p[i]!=pl[i])
				{
					flag=false;
					break;
				}
			}
			if(flag)break;
			for(int i=1;i<=n;i++)tmp[i]=p[i];
		}
		int t=cnt-s%cnt;
		if(t==cnt)t=0;
		while(t--)
		{
			for(int i=1;i<=n;i++)
			{
				p[i]=pl[pl[i]];
			}
			for(int i=1;i<=n;i++)
			{
				pl[i]=p[i];
			}
		}
		for(int i=1;i<=n;i++)printf("%d\n",p[i]);
	}
}


Leonardo's Notebook
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 2395 Accepted: 1017

Description

— I just bought Leonardo's secret notebook! Rare object collector Stan Ucker was really agitated but his friend, special investigator Sarah Kepticwas unimpressed. 
— How do you know it is genuine? 
— Oh, it must be, at that price. And it is written in the da Vinci code. Sarah browsed a few of the pages. It was obvious to her that the code was a substitution cipher, where each letter of the alphabet had been substituted by another letter. 
— Leonardo would have written the plain-text and left it to his assistant to encrypt, she said. And he must have supplied the substitution alphabet to be used. If we are lucky, we can find it on the back cover! She turned up the last page and, lo and behold, there was a single line of all 26 letters of the alphabet: 
QWERTYUIOPASDFGHJKLZXCVBNM 
— This may be Leonardo's instructions meaning that each A in the plain-text was to be replaced by Q, each B withW, etcetera. Let us see... To their disappointment, they soon saw that this could not be the substitution that was used in the book. Suddenly, Stan brightened. 
— Maybe Leonardo really wrote the substitution alphabet on the last page, and by mistake his assistant coded that line as he had coded the rest of the book. So the line we have here is the result of applying some permutation TWICE to the ordinary alphabet! Sarah took out her laptop computer and coded fiercely for a few minutes. Then she turned to Stan with a sympathetic expression. 
— No, that couldn't be it. I am afraid that you have been duped again, my friend. In all probability, the book is a fake. 

Write a program that takes a permutation of the English alphabet as input and decides if it may be the result of performing some permutation twice. 

Input

The input begins with a positive number on a line of its own telling the number of test cases (at most 500). Then for each test case there is one line containing a permutation of the 26 capital letters of the English alphabet.

Output

For each test case, output one line containing Yes if the given permutation can result from applying some permutation twice on the original alphabet string ABC...XYZ, otherwise output No.

Sample Input

2
QWERTYUIOPASDFGHJKLZXCVBNM
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Sample Output

No
Yes


题意:给出一个置换,问它有没有可能是另一个置换的平方。

思路:

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

结论一: 一个长度为 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… 的元素的连接。


对于一个循环节为奇数的循环,它的平方的循环节仍为原来的奇数;对于一个循环节为偶数的循环,它平方后分成两个循环,且他们的循环节都等于原循环循环节的一半,所以这里就判断循环节相等都是偶数的循环个数是不是等于奇数个,如果等于奇数个,那么输出No,否则输出Yes。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int pl[26];
bool vis[26];
int cnt[30];
void dfs(int i,int t)  
{
	if(vis[i])
	{
		cnt[t]++;
		return;
	}
	vis[i]=1;
	dfs(pl[i],t+1);
}
int main()
{
	int t;
	char s[30];
	scanf("%d",&t);
	while(t--)
	{
		memset(vis,0,sizeof(vis));
		memset(cnt,0,sizeof(cnt));
		scanf("%s",s);
		for(int i=0;i<26;i++)
		{
			pl[i]=(int)(s[i]-'A');
		}
		for(int i=0;i<26;i++)  //循环节编号 
		{
			if(!vis[i])
			{
				dfs(i,0);
			}
		}
		bool flag=true;
		for(int i=2;i<=26;i+=2)
		{
			if(cnt[i]&1)
			{
				printf("No\n");
				flag=false;
				break;
			}
		}
		if(flag)printf("Yes\n");
	}
}


The shuffle Problem
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 2203 Accepted: 733

Description

Any case of shuffling of n cards can be described with a permutation of 1 to n. Thus there are totally n! cases of shuffling. Now suppose there are 5 cards, and a case of shuffle is <5, 3, 2, 1, 4>, then the shuffle will be:

Before shuffling:1, 2, 3, 4, 5
The 1st shuffle:5, 3, 2, 1, 4
The 2nd shuffle:4, 2, 3, 5, 1
The 3rd shuffle:1, 3, 2, 4, 5
The 4th shuffle:5, 2, 3, 1, 4
The 5th shuffle:4, 3, 2, 5, 1
The 6th shuffle:1, 2, 3, 4, 5(the same as it is in the beginning)

You'll find that after six shuffles, the cards' order returns the beginning. In fact, there is always a number m for any case of shuffling that the cards' order returns the beginning after m shuffles. Now your task is to find the shuffle with the largest m. If there is not only one, sort out the one with the smallest order.

Input

The first line of the input is an integer T which indicates the number of test cases. Each test case occupies a line, contains an integer n (1 ≤ n ≤ 100).

Output

Each test case takes a line, with an integer m in the head, following the case of shuffling.
 

Sample Input

2
1
5

Sample Output

1 1
6 2 1 4 5 3


题意:给出一个置换的长度n,求出其阶最大的置换(即当T^k = e时,k最大),若有多种,输出字典序最小的。

思路:

置换的长度等于它的每个循环的长度的最小公倍数,即是将n分解为k个数,使得他们的和为n并且他们的最小公倍数最大,然后根据这k个数构造一个字典序最小的置换。求最小公倍数可以用DP,dp[i][j]表示将i分解为j个数所能得到的最大的最小公倍数,那么dp[i][j]=dp[i-k][j-1]*k/gcd(dp[i-k][j-1],k),求出最小公倍数后,把最小公倍数分解成质因子乘积的形式:p1^k1+p2^k2+....+pi^ki,每个循环的长度即为pi^ki,为了得到字典序最小的置换,当这些循环的长度加起来小于n时,就在置换的前面补上单位置换。循环的构造方法也很简单,为了使字典序最小,对于(i,i+1,...j)这个循环它的值为(i+1,i+2,...i)。还有一点需要注意的是应该把循环的长度排个序,长度小的循环放在前面,这样可使字典序最小。


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
ll dp[105][105];  //将i分解为j个数所能得到的最大的最小公倍数
ll max_lcm[105];
ll pri[105];
int ans[105];
ll gcd(ll a,ll b)
{
	return b?gcd(b,a%b):a;
}
void init()
{
	memset(dp,0,sizeof(dp));
	memset(max_lcm,0,sizeof(max_lcm));
	for(int i=1;i<105;i++)
	{
		dp[i][1]=i;
		max_lcm[i]=i;
	}
	max_lcm[1]=1;
	for(int i=2;i<105;i++)
	{
		for(int j=2;j<=i;j++)
		{
			for(ll k=1;i-k>=j-1;k++)
			{
				dp[i][j]=max(dp[i][j],dp[i-k][j-1]*k/gcd(max(dp[i-k][j-1],k),min(dp[i-k][j-1],k)));
				max_lcm[i]=max(max_lcm[i],dp[i][j]);
			}
		}
	}
}
int main()
{
	init();
	int t;
	int n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		int cnt=0;
		int sum=0;
		ll tmp=max_lcm[n];
		for(int i=2;i*i<=tmp;i++)
		{
			if(tmp%i==0)
			{
				int p=1;
				while(tmp%i==0)
				{
					p*=i;
					tmp/=i;
				}
				pri[cnt++]=p;
				sum+=p;
			}
		}
		if(tmp!=1)
		{
			pri[cnt++]=tmp;
			sum+=tmp;
		}
		sort(pri,pri+cnt);
		sum=n-sum;
		int h;
		for(h=1;h<=sum;h++)
		{
			ans[h]=h;
		}
		for(int i=0;i<cnt;i++)
		{
			for(int j=h;j<h+pri[i]-1;j++)
			{
				ans[j]=j+1;
			}
			ans[h+pri[i]-1]=h;
			h+=pri[i];
		}
		printf("%d ",max_lcm[n]);
		for(int i=1;i<n;i++)printf("%d ",ans[i]);
		printf("%d\n",ans[n]);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值