P1338 末日的传说
題目描述
只要是參加jsoi活動的同學一定都聽說過Hanoi塔的傳說:三根柱子上的金片每天被移動一次,當所有的金片都被移完之后,世界末日也就隨之降臨了。
在古老東方的幻想鄉,人們都采用一種奇特的方式記錄日期:他們用一些特殊的符號來表示從1開始的連續整數,1表示最小而N表示最大。創世紀的第一天,日歷就被賦予了生命,它自動地開始計數,就像排列不斷地增加。
我們用1-N來表示日歷的元素,第一天日歷就是
1, 2, 3, … N
第二天,日歷自動變為
1, 2, 3, … N, N-1
……每次它都生成一個以前未出現過的“最小”的排列——把它轉為N+1進制后數的數值最小。
日子一天一天地過著。有一天,一位預言者出現了——他預言道,當這個日歷到達某個上帝安排的時刻,這個世界就會崩潰……他還預言到,假如某一個日期的逆序達到一個值M的時候,世界末日就要降臨。
什么是逆序?日歷中的兩個不同符號,假如排在前面的那個比排在后面的那個更大,就是一個逆序,一個日期的逆序總數達到M后,末日就要降臨,人們都期待一個賢者,能夠預見那一天,到底將在什么時候到來?
輸入輸出格式
輸入格式:
?
只包含一行兩個正整數,分別為N和M。
?
輸出格式:
?
輸出一行,為世界末日的日期,每個數字之間用一個空格隔開。
?
輸入輸出樣例
輸入樣例#1:5 4 輸出樣例#1:
1 3 5 4 2
說明
對于10%的數據有N <= 10。
對于40%的數據有N <= 1000。
對于100%的數據有 N <= 50000。
所有數據均有解。
?
我們考慮把這個問題縮小范圍。
比如n=5,在決定了最小的數“1”的位置之后,剩下的幾個數是2 3 4 5,但是他們
具體是多少沒必要關心,我們只要關心他們的相對大小關系。
所以考慮完當前最小的數,算出這個數對答案的貢獻,然后減掉這個貢獻,
就可以轉而解決一個更小的子問題。(即n-->n-1)
回到題目上,要求是求一個有m個逆序對的字典序最小的排列。
我們知道一個長度為n的排列最多有(n-1)*n/2個逆序對,也知道一個排列的逆序對數越多,排列字典序越大。
所以如果當前m不比當前的(n-2)*(n-1)/2(也就是減少一個數之后的最多的逆序對數)大,
就可以直接把當前的最小數放在最前面,這肯定是最優的。
反之,則考慮最小數的放置位置。
假設當前排列長為n,最小數為a,則a有n種放法,放在從左到右第i個位置時會生成i-1個逆序對
(因為它左邊有i-1個比他大)。
因為m大于n-1長度排列最多所能產生的逆序數,所以a不可能放在最前面,否則不滿足條件。
怎么辦呢?想到之前說的逆序對越多字典序越大,我們就必須讓剩下的數能構成的逆序對數盡量小,所以a要放到最后,這樣m減少的最多。
放完了a,問題就變成了n-1和m-(a的貢獻)的子問題,遞歸求解即可。時間復雜度O(n)。
?
?
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define lli long long int using namespace std; const lli MAXN=50001; inline void read(lli &n) {char c='+';lli x=0;bool flag=0;while(c<'0'||c>'9'){c=getchar();if(c=='-')flag=1;}while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48,c=getchar();}flag==1?n=-x:n=x; } lli a[MAXN]; lli ed,bg; int main() {lli n,m;read(n);read(m);ed=n;bg=1;for(lli i=1;i<=n;i++){lli num=(n-i)*(n-i-1)/2;if(num>=m)a[bg++]=i;else a[ed--]=i,m-=(ed-bg+1);}for(lli i=1;i<=n;i++)printf("%lld ",a[i]);return 0; }
總結
以上是生活随笔為你收集整理的P1338 末日的传说的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在WebStorm里配置watcher实
- 下一篇: 过滤器实例——字符编码Filter