Codeforces1443 E. Long Permutation(康托展开及其逆运算)

题意:

给定n,q,一开始有一个长度为n的原排列,
q次操作,操作有两种:
(1 l r):计算[l,r]的和。
(2 x):将排列替换为下x个排列(向后推x次),x<=1e5

数据范围:n<=2e5,q<=2e5

解法:
因为q<=2e5,x<=1e5,因此排列最多向后推2e10,15!>2e10,因此只有最后15个数会变化.

对于后15个数,可以用康托展开及其逆运算快速替换为下x个排列,
康托展开的复杂度为O(n^2),利用数据结构可以优化到O(nlogn),这题只有15可以不优化.

区间和可以用前缀和快速计算.
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
int sum[maxm];
int a[maxm];
int b[maxm];
int fac[20];
int n,q;
int m;
void update(int x){
    //康托展开
    int c=0;
    for(int i=n-m+1;i<=n;i++){
        b[++c]=a[i]-(n-m);
    }
    int now=0;
    for(int i=1;i<=m;i++){
        int cnt=0;
        for(int j=i+1;j<=m;j++){
            if(b[i]>b[j]){
                cnt++;
            }
        }
        now+=fac[m-i]*cnt;
    }
    //向后推x次
    now+=x;
    //康托展开逆运算
    int mark[20]={0};
    for(int i=1;i<=m;i++){
        int t=now/fac[m-i];
        now%=fac[m-i];
        int j;
        for(j=1;j<=m;j++){
            if(!mark[j]){
                if(t==0)break;
                t--;
            }
        }
        b[i]=j;
        mark[j]=1;
    }
    c=n-m+1;
    for(int i=1;i<=m;i++){
        a[c++]=b[i]+(n-m);
    }
    //
    for(int i=n-m+1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
    }
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    //
    fac[0]=1;
    for(int i=1;i<=15;i++){
        fac[i]=fac[i-1]*i;
    }
    //
    cin>>n>>q;
    m=min(n,15LL);//只需要操作后m个元素
    for(int i=1;i<=n;i++){
        a[i]=i;
        sum[i]=sum[i-1]+a[i];
    }
    while(q--){
        int op;cin>>op;
        if(op==1){
            int l,r;cin>>l>>r;
            cout<<sum[r]-sum[l-1]<<endl;
        }else{
            int x;cin>>x;
            update(x);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值