JZOJ3232. 【佛山市选2013】排列
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 ,按照某一規則操作若干次后,使得這個數列最后變成有序數列。求最小操作且字典序最小的序列。
題解
很顯然,我們可以將數列劃分為若干個大小互質的環,這種情況下,答案顯然就是他們的乘積。因此,問題就轉化為,給定一個數n,求若干個和為 n 的互質數的最大乘積。這個可以DP解決,設f[i][j]表示到第 i 個質數,和為j的最大乘積。再記錄一個前驅。至于方案如何構造,這個就自己思考一下。
SRC
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> using namespace std ;#define N 10000 + 10 #define M 1300 + 10bool 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 ; }以上.
總結
以上是生活随笔為你收集整理的JZOJ3232. 【佛山市选2013】排列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: word2vec 如何获得当前的所有词向
- 下一篇: 记忆力下降