考虑算出有多少个不同的排列,考虑 dp,\(f_{i,j}\) 表示后缀 \([i,n]\),还有 \(j\) 的代价可以使用,转移为 \(f_{i,j}=\sum \limits_{k=0}^{\min(n-i,j)}f_{i+k+1,j-k}\)。
然后考虑如何算出第 \(j\) 个排列的具体值,这个很好计算,考虑从 \(1\) 开始,从小到大枚举第一个字符,然后对应翻转即可。
暴力做是 \(O(nq)\) 的,由于 \(c\leq 4\),我们至多翻转 \(c\) 次,其他情况我们都是不变的,我们希望快速处理不反转的情况,直接处理倍增数组即可。时间复杂度 \(O(nc^2+qc\log n)\)。
比较难写,vp 后两分钟发现有两行写反了。
#include<bits/stdc++.h>
using namespace std;
#define N 30005
#define ll long long
#define fr first
#define se second
int t,n,q,c,a[N];
ll f[N][6];
pair<ll,ll> go[N][6][16];//(l,r]
void work(){
scanf("%d%d%d",&n,&c,&q);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=0;i<=c;i++)f[n+1][i]=1;
for(int i=n;i;i--){
for(int j=0;j<=c;j++){
f[i][j]=0;
for(int k=0;k<=min(n-i,j);k++)f[i][j]+=f[i+k+1][j-k];
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=c;j++){
go[i][j][0].fr=0;
for(int k=0;k<=min(n-i,j);k++)if(a[i+k]<a[i])go[i][j][0].fr+=f[i+k+1][j-k];
go[i][j][0].se=go[i][j][0].fr+f[i+1][j];
}
}
for(int k=1;k<=14;k++){
for(int i=1;i+(1<<k)-1<=n;i++){
for(int j=0;j<=c;j++){
go[i][j][k].fr=go[i][j][k-1].fr+go[i+(1<<k-1)][j][k-1].fr;
go[i][j][k].se=go[i][j][k-1].fr+go[i+(1<<k-1)][j][k-1].se;
}
}
for(int i=max(n-(1<<k)+2,1);i<=n;i++)for(int j=0;j<=c;j++)go[i][j][k]={0,0};
}
while(q--){
int i,r=c;ll j;
scanf("%d%lld",&i,&j);
if(j>f[1][c]){puts("-1");continue;}
int p=1;
while(p<=i){
for(int k=14;k>=0;k--)if(p+(1<<k)<=i&&go[p][r][k].fr<j&&j<=go[p][r][k].se)j-=go[p][r][k].fr,p+=(1<<k);
if(p==i&&go[p][r][0].fr<j&&j<=go[p][r][0].se){printf("%d\n",a[p]);break;}
int b[10]={0},m=0,to;
for(int k=0;k<=min(n-p,r);k++)b[++m]=p+k;
sort(b+1,b+1+m,[](const int x,const int y){return a[x]<a[y];});
for(int i=1;i<=m;i++)
if(j>f[b[i]+1][r-(b[i]-p)])j-=f[b[i]+1][r-(b[i]-p)];
else{
to=b[i];
break;
}
if(p<=i&&i<=to){
printf("%d\n",a[to-(i-p)]);
break;
}
r-=to-p;
p=to+1;
}
}
}
int main(){
scanf("%d",&t);
while(t--)work();
}