考虑算出有多少个不同的排列,考虑 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();
}