康託展開及其逆展開
阿新 • • 發佈:2022-04-04
康託展開
可以用於求 全排列 的排名(字典序)。
我們先給定一組排列。這裡為了舉例方便就拿了 OI-wiki 上的例子 \(\{2,5,3,4,1\}\) 了。
我們第一位是 \(2\) ,那麼以 \(1\) 開頭的就都比它小。後面的也是同理的搞法,就是看當前這位有多少個比它小還之前沒有出現過的數(因為要保證是排列)。由於高位上的以及統計過了,我們只需要統計當前位下的即可。具體來說,多少個比它小還之前沒有出現過的數的個數乘上目前長度 \(-1\) 的階乘就是比它小的排列個數。
我們每位如此統計完加起來,然後再 \(+1\) (因為是排名)即可。這個過程我們用樹狀陣列維護,複雜度 \(O(n\log n)\)
逆康託展開
其實我們得知一個排列的排名後也可以得到其原本的排列。
我們先對於排名自減一次。然後我們每次對於長度 \(-1\) 的階乘做除法,得到餘數 \(r\) 和除數 \(x\) ,那麼這一位上的數就應該是之前沒出現過的數中的第 \(x+1\) 名,找到它並且把排名賦值為 \(r\) 進入子問題。
一直做長度次即可。這個過程我們用權值線段樹維護可以做到 \(O(n\log n)\)。
總的參考程式碼:
#include<bits/stdc++.h> #define ll long long #define db double #define filein(a) freopen(#a".in","r",stdin) #define fileot(a) freopen(#a".out","w",stdout) #define sky fflush(stdout); #define gc getchar #define pc putchar namespace IO{ inline bool blank(const char &c){ return c==' ' or c=='\n' or c=='\t' or c=='\r' or c==EOF; } inline void gs(char *s){ char ch=gc(); while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {*s++=ch;ch=gc();} *s=0; } inline void gs(std::string &s){ char ch=gc();s+='#'; while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {s+=ch;ch=gc();} } inline void ps(char *s){ while(*s!=0) pc(*s++); } inline void ps(const std::string &s){ for(auto it:s) if(it!='#') pc(it); } template<class T> inline void read(T &s){ s=0;char ch=gc();bool f=0; while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();} while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();} if(ch=='.'){ db p=0.1;ch=gc(); while('0'<=ch&&ch<='9') {s=s+p*(ch^48);p*=0.1;ch=gc();} } s=f?-s:s; } template<class T,class ...A> inline void read(T &s,A &...a){ read(s);read(a...); } }; using IO::read; using IO::gs; using IO::ps; const int N=1e6+3,mods=998244353; inline int inc(int x,int y){ return (x+=y)>=mods?x-mods:x; } inline int dec(int x,int y){ return (x-=y)<0?x+mods:x; } inline int mul(int x,int y){ return 1ll*x*y%mods; } int n; struct BitTree{ int c[N]; inline void add(int x,int k){ for(;x<=n;x+=(x&-x) ) c[x]+=k; } inline int find(int y){ int res=0; for(;y;y-=(y&-y) ) res=inc(res,c[y]); return res; } }t1; struct SegTree{ struct node{ int l,r,mid; int cnt; }t[N<<2]; inline void pushup(int i){ t[i].cnt=t[i<<1].cnt+t[i<<1|1].cnt; } inline void build(int i,int l,int r){ t[i].l=l;t[i].r=r;t[i].mid=(l+r)>>1; t[i].cnt=1; if(l==r){ return; } build(i<<1,l,t[i].mid); build(i<<1|1,t[i].mid+1,r); pushup(i); } inline int kth(int i,int k){ if(t[i].l==t[i].r){ if(k!=1) return -1; return t[i].l; } int key=t[i<<1].cnt; if(key>=k) return kth(i<<1,k); return kth(i<<1|1,k-key); } inline void modify(int i,int p,int k){ if(p<=t[i].l and t[i].r<=p){ t[i].cnt+=k; return; } if(p<=t[i].mid) modify(i<<1,p,k); if(t[i].mid+1<=p) modify(i<<1|1,p,k); pushup(i); } }t2; int fac[N]; inline int Contor(int *a){ memset(t1.c,0,sizeof(t1.c) ); int rk=0; for(int i=1;i<=n;++i){ t1.add(a[i],1); rk=inc(rk,mul(dec(a[i],t1.find(a[i]) ),fac[n-i]) ); } return rk+1; } inline void InvContor(int x,int *a){ t2.build(1,1,n);--x; for(int i=1;i<=n;++i){ int r=x/fac[n-i]; x%=fac[n-i]; a[i]=t2.kth(1,r+1); t2.modify(1,a[i],-1); } } int a[N],b[N]; int main(){ filein(a);fileot(a); read(n); fac[0]=fac[1]=1; for(int i=2;i<=n;++i){ fac[i]=mul(fac[i-1],i); } for(int i=1;i<=n;++i){ read(a[i]); } int rk=Contor(a); printf("%d\n",rk); InvContor(rk,b); for(int i=1;i<=n;++i){ printf("%d ",b[i]); }pc('\n'); return 0; }