题意:给你一个n和q,1~n个数和q次查询
n表示1到n的全排列,一开始是1,2,3····n-1,n
查询有两种
1 x
表示将当前的排列往后递推x次,比如现在是[1,2,3,4]->[1,2,4,3](这是递推了1次)按照字典序
2 x y
输出排列中第x个数到第y个数的和
思路:
我们晓得全排列的复杂度是n!
比如5的全排列有5!=120种
但题目给的x是1e5的,并且q是2e5,所以题目给的排列最坏情况下会递推x*q次,也就是2e10次
而14个数的全排列为14!高达87178291200次!
所以最多会改变排列的最后14个数
所以利用逆康托展开计算出第x个排列为多少,并同时更新前缀和即可
关于康托展开(自行学习)
代码:
#include <bits/stdc++.h>
#define pb push_back
#define ll long long
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
template<class T> inline void read(T &x){
x=0; register char c=getchar(); register bool f=0;
while(!isdigit(c))f^=c=='-',c=getchar();
while(isdigit(c))x=x*10+c-'0',c=getchar(); if(f)x=-x;
}
using namespace std;
ll n,q,tot,op,x,y;
ll pre[200005],vis[30];
ll c[2000005];
ll book[2000];
ll Z(int x)
{
ll s=1;
for(int i=1;i<=x;i++){s*=(ll)i;}
return s;
}
int main()
{
scanf("%lld %lld",&n,&q);
ll id=1;
if(n>14)
{
for(int i=n-13;i<=n;i++){vis[++tot]=(ll)i;}//映射
for(int i=1;i<=n;i++){pre[i]=pre[i-1]+(ll)i;}//前缀和
while(q--)
{
scanf("%lld",&op);//操作数
if(op==1)
{
scanf("%lld %lld",&x,&y);
printf("%lld\n",pre[y]-pre[x-1]);
continue;
}
else
{
scanf("%lld",&x);
id+=x;
ll tmp=id-1;//tmp表示前面有几个比此排列小的
for(int i=1;i<=20;i++){book[i]=0;}
for(int i=1;i<=13;i++)//计算每一位
{
ll base=tmp/Z(14-i);//下取整
for(int j=1;j<=14;j++)
{
if(book[j]==0&&base==0)
{c[i]=j;break;}
if(book[j]==0)
{base--;}
}
book[c[i]]=1;
ll cnt=0;
for(int j=1;j<c[i];j++)
{
if(book[j]==0){cnt++;}
}
tmp-=cnt*Z(14-i);
}
for(int i=1;i<=14;i++)//计算最后一位
{
if(book[i]==0){c[14]=i;break;}
}
for(int i=n-13;i<=n;i++)//更新前缀和
{
pre[i]=pre[i-1]+vis[c[i-n+14]];
}
}
}
}
else
{
for(int i=1;i<=n;i++){pre[i]=pre[i-1]+(ll)i;}
while(q--)
{
scanf("%lld",&op);
if(op==1)
{
scanf("%lld %lld",&x,&y);
printf("%lld\n",pre[y]-pre[x-1]);
}
else
{
scanf("%lld",&x);
id+=x;
ll tmp=id-1;
for(int i=1;i<=20;i++){book[i]=0;}
for(int i=1;i<=n-1;i++)
{
ll base=tmp/Z(n-i);
for(int j=1;j<=n;j++)
{
if(base==0&&book[j]==0)
{c[i]=(ll)j;break;}
if(book[j]==0){base--;}
}
book[c[i]]=1;
ll cnt=0;
for(int j=1;j<c[i];j++)
{
if(book[j]==0){cnt++;}
}
tmp-=cnt*Z(n-i);
}
for(int i=1;i<=n;i++)
{
if(book[i]==0){c[n]=i;break;}
}
for(int i=1;i<=n;i++)
{
pre[i]=pre[i-1]+c[i];
}
}
}
}
return 0;
}