• 洛谷P5367 【模板】康托展开


    题目链接:https://www.luogu.com.cn/problem/P5367

    康拓展开:建立起排列和自然数的双射关系,可以用来求解排列的哈希值

    设给定一个排列 (a),则 (a) 在排列集合中的排名为:

    [ans=∑_{i=1}^npi∗(n−i)! ]

    其中 (p_i) 表示小于 (a[i]) 的没有出现过的数的次数 (可以用树状数组求解)

    时间复杂度 (O(nlogn))

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1000100;
    const int M = 998244353;
    
    int n;
    int a[maxn], c[maxn];
    int fac[maxn];
    
    void add(int k, int x){
    	for(; x <= n ; x += x & (-x)){
    		c[x] += k;
    	}
    }
    
    int query(int x){
    	int res = 0;
    	for(; x ; x -= x & (-x)){
    		res += c[x];
    	}
    	return res;
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
    	n = read();
    	fac[0] = 1;
    	for(int i = 1 ; i <= n ; ++i){ fac[i] = 1ll * fac[i - 1] * i % M; } 
    	for(int i = 1 ; i <= n ; ++i) add(1, i);
    	
    //	printf("%d
    ",query(n));
    	for(int i = 1 ; i <= n ; ++i) a[i] = read();
    	
    	int ans = 1;
    	for(int i = 1 ; i <= n ; ++i){
    		int p = 1ll * (query(a[i]) - 1) * fac[n - i] % M;
    		ans = (ans + p) % M;
    		add(-1, a[i]);
    	}
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    

    逆康托展开

    将求解康托展开的过程反过来,每次对 (ans) 除和模 ((n-i)!) 即可
    需要求解动态第 (k) 小元素,使用平衡树

  • 相关阅读:
    java ssh整合报错:java.lang.NoSuchMethodError: antlr.collections.AST.getLine()I
    Ubuntu16.04安装搜狗输入法后有黑边问题的解决方法
    socket
    vim编辑器的使用
    linux用户和群组
    bash shell
    [LightOJ 1128]Greatest Parent
    [Luogu P4180][BJWC 2010]严格次小生成树
    函数、方法区别
    有关_meta内容(持续更新)
  • 原文地址:https://www.cnblogs.com/tuchen/p/14024358.html
Copyright © 2020-2023  润新知