题意:
给定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;
}