康托展开

康托展开

  • 用途:求\(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\)位,重复这个步骤。

然后……没了。

有个多重集康托展开的例题

[HAOI2010] 计数

话说学康托展开纯粹是因为这题不想写数位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;
}
posted @ 2024-07-03 16:12  CuFeO4  阅读(8)  评论(0编辑  收藏  举报