JZOJ3232. 【佛山市选2013】排列
Description
一个关于n个元素的排列是指一个从{1, 2, …, n}到{1, 2, …, n}的一一映射的函数。这个排列p的秩是指最小的k,使得对于所有的i = 1, 2, …, n,都有p(p(…p(i)…)) = i(其中,p一共出现了k次)。
例如,对于一个三个元素的排列p(1) = 3, p(2) = 2, p(3) = 1,它的秩是2,因为p(p(1)) = 1, p(p(2)) = 2, p(p(3)) = 3。
给定一个n,我们希望从n!个排列中,找出一个拥有最大秩的排列。例如,对于n=5,它能达到最大秩为6,这个排列是p(1) = 4, p(2) = 5, p(3) = 2, p(4) = 1, p(5) = 3。
当我们有多个排列能得到这个最大的秩的时候,我们希望你求出字典序最小的那个排列。对于n个元素的排列,排列p的字典序比排列r小的意思是:存在一个整数i,使得对于所有j < i,都有p(j) = r(j),同时p(i) < r(i)。对于5来说,秩最大而且字典序最小的排列为:p(1) = 2, p(2) = 1, p(3) = 4, p(4) = 5, p(5) = 3。
Input
输入的第一行是一个整数T(T <= 10),代表数据的个数。
每个数据只有一行,为一个整数N。
Output
对于每个N,输出秩最大且字典序最小的那个排列。即输出p(1), p(2),…,p(n)的值,用空格分隔。
Sample Input
2
5
14
Sample Output
2 1 4 5 3
2 3 1 5 6 7 4 9 10 11 12 13 14 8
Data Constraint
对于40%的数据,有1≤N≤100。
对于所有的数据,有1≤N≤10000。
Solution
题目大意
给一个数n,按照某一规则操作若干次后,使得这个数列最后变成有序数列。求最小操作且字典序最小的序列。
题解
很显然,我们可以将数列划分为若干个大小互质的环,这种情况下,答案显然就是他们的乘积。因此,问题就转化为,给定一个数
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std ;
#define N 10000 + 10
#define M 1300 + 10
bool bz[N] ;
double f[M][N] ;
int P[M] , a[N] , ans[N] , Ask[20] , g[M][N] ;
int T , n ;
void Pre() {
for (int i = 2 ; i <= n ; i ++ ) {
if ( !bz[i] ) {
bz[i] = 1 ;
P[++P[0]] = i ;
}
for (int j = 1 ; j <= P[0] ; j ++ ) {
if ( i * P[j] > n ) break ;
bz[i*P[j]] = 1 ;
if ( i % P[j] == 0 ) break ;
}
}
}
void DP() {
for (int i = 0 ; i < P[0] ; i ++ ) {
for (int j = 0 ; j <= n ; j ++ ) {
if ( f[i][j] > f[i+1][j] ) {
f[i+1][j] = f[i][j] ;
g[i+1][j] = j ;
}
int k = P[i+1] ;
double del = log(P[i+1]) , t = del ;
for ( ; k + j <= n ; k *= P[i+1] , t += del ) {
if ( f[i][j] + t >= f[i+1][j+k] ) {
f[i+1][j+k] = f[i][j] + t ;
g[i+1][j+k] = j ;
}
}
}
}
}
void solve() {
double mx = 0 ;
int now = n ;
a[0] = 0 ;
for (int i = 1 ; i <= n ; i ++ )
if ( f[P[0]][i] > mx ) {
mx = f[P[0]][i] ;
now = i ;
}
for (int i = 1 ; i <= n - now ; i ++ ) a[++a[0]] = 1 ;
int i = P[0] ;
while ( i ) {
a[++a[0]] = now - g[i][now] ;
now = g[i][now] ;
i -- ;
}
}
void find() {
sort( a + 1 , a + a[0] + 1 ) ;
int w = 0 , k = 1 ;
while ( !a[k] && k <= P[0] ) k ++ ;
for (int i = 1 ; i <= n ; i ++ ) {
if ( i == w + a[k] ) {
ans[i] = w + 1 ;
w += a[k] ;
k ++ ;
} else ans[i] = i + 1 ;
}
}
void print() {
for (int i = 1 ; i < n ; i ++ ) printf( "%d " , ans[i] ) ;
printf( "%d\n" , ans[n] ) ;
}
int main() {
scanf( "%d" , &T ) ;
memset( f , 0 , sizeof(f) ) ;
memset( g , 0 , sizeof(g) ) ;
for (int i = 1 ; i <= T ; i ++ ) {
scanf( "%d" , &Ask[i] ) ;
n = max( n , Ask[i] ) ;
}
Pre() ;
DP() ;
for (int i = 1 ; i <= T ; i ++ ) {
n = Ask[i] ;
solve() ;
find() ;
print() ;
}
return 0 ;
}
以上.