康托展开
康托展开
-
用途:求\(1\sim n\)的任意一个排列的排名
-
时间复杂度\(O(n\log n)\) (树状数组优化)
根据示例解释一下:
有一个长度为5的排列[2,4,5,3,1],大于以1为第一位的所有5排列,所以排名加了\(1\times4!\)
大于所有以2为第一位,\(1\sim 3\)为第二位的排列,又因为2已经为第一位,所以排除2,排名加了\(2\times 3!\)
以此类推,排名为\(1\times 4! + 2\times 3! +2\times 2! +1\times1! + 0\times0! + 1 = 42\)
\(code:\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
char *p1,*p2,buf[1<<20];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(long double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(float &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
inline void write(char x){pc(x);}
inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
const int N = 1e6+10,mod = 998244353;
int fac[N],n;//fac 预处理阶乘,
#define lowbit(x) (x&(-x))
int tree[N];//树状数组,记录每个数字是否出现
inline void update(int pos,int val){
for(int i = pos;i <= n; i += lowbit(i)) tree[i] += val;
}
inline int query(int pos){
int res = 0;
for(int i = pos; i;i -= lowbit(i)) res += tree[i];
return res;
}
signed main(){
#ifndef ONLINE_JUDGE
infile("in.in");outfile("out.out");
#else
#endif
read(n);
fac[0] = 1;
for(int i = 1;i <= n; ++i) fac[i] = 1ll * fac[i-1] * i % mod;
for(int i = 1;i <= n; ++i) update(i,1);
ll ans = 0;
for(int i = 1,x;i <= n; ++i){
read(x);
update(x,-1);//将x减去
ans = (ans + 1ll * query(x) * fac[n - i] % mod) % mod;
}
write((ans + 1) % mod);
}
逆康托展开
还是以上述例子为例,\(42-1=41\),\(\left\lfloor\frac{41}{4!}\right\rfloor = 1\),所以第一位为2,让排名减去\(1\times 4!\),得到17,\(\left\lfloor\frac{17}{3!}\right\rfloor = 2\)
所以第二位为4,以此类推,第三位为5,第四位为3,第五位为1
可以用权值线段树做,平衡树好像也可以,复杂度都是\(O(n\log n)\)
\(code:\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
char *p1,*p2,buf[1<<20];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(long double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(float &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
inline void write(char x){pc(x);}
inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
constexpr int N = 1e5+10;
class FHQ_Treap{
private:
struct fhq_treap{
int ls,rs,val,pri,size;
#define ls(x) tree[x].ls
#define rs(x) tree[x].rs
#define val(x) tree[x].val
#define pri(x) tree[x].pri
#define size(x) tree[x].size
}tree[N];
int tot;
public:
int root;
inline void pushup(int p){size(p) = size(ls(p))+size(rs(p))+1;}
inline int New(int val){
++tot;
val(tot) = val;
size(tot) = 1;
pri(tot) = rand();
return tot;
}
int merge(int x,int y){
if(!x||!y) return x|y;
if(pri(x) < pri(y)) {rs(x) = merge(rs(x),y),pushup(x);return x;}
else {ls(y) = merge(x,ls(y)),pushup(y);return y;}
}
void split(int p,int k,int &x,int &y){
if(!p) return x = y = 0,void();
if(val(p) <= k) x = p,split(rs(p),k,rs(x),y);
else y = p,split(ls(p),k,x,ls(y));
pushup(p);
}
inline void insert(int val){
int x,y;
split(root,val,x,y);
root = merge(merge(x,New(val)),y);
}
inline void erase(int val){
int x,y,z;
split(root,val,x,y);
split(x,val-1,x,z);
z = merge(ls(z),rs(z));
root = merge(merge(x,z),y);
}
inline int rank(int val){
int x,y;
split(root,val-1,x,y);
int res = size(x)+1;
root = merge(x,y);
return res;
}
inline int kth(int x,int k){
if(!x) return 0;
if(size(ls(x)) >= k) return kth(ls(x),k);
else if(size(ls(x))+1 >= k) return val(x);
else return kth(rs(x),k-size(ls(x))-1);
}
inline int get_pre(int val){
int x,y;
split(root,val-1,x,y);
int res = kth(x,size(x));
root = merge(x,y);
return res;
}
inline int get_nxt(int val){
int x,y;
split(root,val,x,y);
int res = kth(y,1);
root = merge(x,y);
return res;
}
inline void clear(){
memset(tree,0,sizeof tree);
tot = 0;
}
}T;
ll fac[N];
signed main(){
#ifndef ONLINE_JUDGE
infile("in.in");outfile("out.out");
#else
#endif
srand(time(0));
int n,m;read(n,m);
fac[0] = 1;
for(int i = 1;i <= n; ++i) fac[i] = fac[i-1]*i;
while(m--){
T.clear();
for(int i = 1;i <= n; ++i) T.insert(i);
int rk;read(rk);rk--;
for(int j = n - 1;j >= 0; --j){
int res = rk/fac[j];
int num = T.kth(T.root,res + 1);
write(num,' ');
rk -= (T.rank(num) - 1) * fac[j];
T.erase(num);
}
write('\n');
}
}
突然发现自己不会写线段树了,一怒之下贺了平衡树板子
这篇代码抗住了几千组对拍,应该没有问题吧……(如有问题,感谢指正)
多重集康托展开
前置知识:多重集的全排列
假设一共有\(n\)个元素,第\(i\)种元素有\(cnt_i\)个,则排列数为\(\frac{(\sum_{i=1}^{n}cnt_i)!}{\prod_{i=1}^{n}(cnt_i!)}\)
和普通的康托展开思路一样
对于第\(i\)位,如果我们放置字典序小于原排列的元素,那么后面的所有元素的排列数就是多重集的全排列。
接下来,我们第\(i\)位放置和原排列相同的元素,再递推到\(i+1\)位,重复这个步骤。
然后……没了。
有个多重集康托展开的例题
话说学康托展开纯粹是因为这题不想写数位dp
\(solution:\)
先不考虑0,我们发现这就是一个多重集的康托展开。
现在考虑0,我们可以准备很多个0,若用上0就插入,用不上就扔在最前面,显然这不影响答案。
懒得打高精,可以用\(Python\)或 \(\_\_int128\_t\)水过
\(code:\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
char *p1,*p2,buf[1<<20];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(long double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(float &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
inline void write(char x){pc(x);}
inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
#define int __int128_t
inline int max(int a,int b){return (a > b) ? a : b;}
const int N = 55;
int fac[N],h[N],n,a[N],mx = 0;
char s[N];
inline void init(int n){
fac[0] = 1;
for(int i = 1;i <= n; ++i) fac[i] = fac[i-1] * i;
}
inline int solve(){
int sum = 0;
for(int i = 1;i <= n; ++i){
for(int j = 1;j <= n; ++j) h[a[j]] = 0;
for(int j = i;j <= n; ++j) h[a[j]]++;
for(int j = 0;j < a[i]; ++j){
if(h[j]){
h[j]--;
int x = fac[n - i];
for(int k = 0;k <= mx; ++k) x /= fac[h[k]];
sum += x;
h[j]++;
}
}
}
return sum;
}
signed main(){
#ifndef ONLINE_JUDGE
infile("in.in");outfile("out.out");
#else
#endif
scanf("%s",s+1);
n = strlen(s + 1);
for(int i = 1;i <= n; ++i)
a[i] = s[i] - '0',mx = max(mx,a[i]);
init(max(mx,n));
write(solve());
}
话说我用自己写的快读就\(WA\)了,有好心人帮忙调调嘛
inline bool read(char *s){
int len=0;char x=gc();
for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;
for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;
s[len]='\0';
return true;
}
本文来自博客园,作者:CuFeO4,转载请注明原文链接:https://www.cnblogs.com/hzoi-Cu/p/18281525