標籤:style blog color os for io
我們知道1——k有K!種排列,現在給定k和n,要你按字典序輸出 第n種排列的數列
而且題目給的 n是 n=S1(k-1)!+S2(k-2)!+...+Sk-1*1!+Sk*0!(0=<Si<=k-i),
k最大為50000,直接算出來不可能,我發現這個n的運算式很有意思,明顯(k-1)!就是表示第一次除第一個以外,剩下k-1個數的排列個數,乘一個S1就說明之前用了多少次這種排列了,根據這個 再手算了一下,發現就是,每次找第Sk個數,輸出,當然已經訪問過的數就要T出去
這很明顯就是樹狀數組 (或者線段樹的單點修改和查詢)功能,先初始化每個都是大小為1,然後每次二分找第Sk個,找到之後,把該位置置為-1,消除該位的影響,繼續找,繼續輸出即可。你看題目裡給定的S的範圍也就是順應這個,故意把剩下幾個數,S就恰好限定在這個裡面
發現自己二分寫的還不是很熟,一開始還搞了一下子,是用mid還是L做結果呢 最後在我的寫法裡被證明是用L做結果
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define N 50000+10using namespace std;int A[N];int n;int lowbit(int x){ return x&(-x);}void add(int x,int v){ while (x<=n){ A[x]+=v; x+=lowbit(x); }}void init(){ for (int i=1;i<=n;i++){ add(i,1); }}int query(int x){ int ret=0; while (x>0){ ret+=A[x]; x-=lowbit(x); } return ret;}int main(){ int t; scanf("%d",&t); while (t--) { scanf("%d",&n); init(); for (int i=1;i<=n;i++){ int a; scanf("%d",&a); int L=1,R=n,mid; while (L<R) { mid=(L+R)>>1; int tmp=query(mid); //if (tmp==a+1) break; if (tmp<a+1) L=mid+1; else R=mid; } //cout<<"@@"<<mid<<endl; add(L,-1); printf("%d",L); if (i<n) putchar(‘ ‘); } puts(""); } return 0;}