先考虑
p=q
p
=
q
的情况,习惯先把求第
k
k
大变成求第小。
那么我们逐个确定第
k
k
小的串每种数字包含了多少个。假设当前我们已经确定了之前的数的个数,此时对于每个左端点
i
i
,合法的右端点都是一个区间。现在考虑二分确定
x
x
的个数,我们把序列中为的位置单独挑出来,这些位置把序列分成若干段,假如二分有
mid
m
i
d
个
x
x
,那么对于每一段,合法的右端点又会有一个小于之后第的个
x
x
的位置的一个限制,然后再求合法区间个数。最后我们成功得到的个数为
num
n
u
m
之后,每一段的合法右端点区间就要和其往后第
num
n
u
m
段求交。
以上这些区间求交操作就是对
li
l
i
chkmax或者对
ri
r
i
chkmin,又因为
li,ri
l
i
,
r
i
是随着
i
i
单调不降的,我们可以用一棵线段树维护这些。
求出第
p
p
小之后,考虑如何推到第小。注意到左端点固定时,随着右端点增大,串的大小也是增大的,于是我们可以找出每一个左端点第一个比
p
p
大的右端点并用一个堆来维护他们,每次弹出最小的之后再把
[l,r+1]
[
l
,
r
+
1
]
加入堆。至于比较两个串的大小,可以用一棵主席树维护hash值,在上面二分找到第一个hash值不同的地方即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define N 100010
#define ll long long
#define min(x,y) (x<y?x:y)
#define max(x,y) (x<y?y:x)
#define mid (l+r>>1)
using namespace std;
const int mod=1000000007,W=11003;
int n,m,a[N],z[N];
vector<int> t[N];
ll k0,k1,pw[N];
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
struct tree2
{
int rt[N],cnt,ch[N<<6][2],len[N<<6];
ll w[N<<6];
void update(int v)
{
w[v]=(w[ch[v][0]]*pw[len[v]>>1]+w[ch[v][1]])%mod;
}
void build(int v,int p,int l,int r,int x)
{
len[v]=r-l+1;
if(l==r) {w[v]=w[p]+1;return;}
if(x<=mid)
{
ch[v][1]=ch[p][1];
ch[v][0]=++cnt;
build(cnt,ch[p][0],l,mid,x);
}
else
{
ch[v][0]=ch[p][0];
ch[v][1]=++cnt;
build(cnt,ch[p][1],mid+1,r,x);
}
update(v);
}
int compare(int pl,int pr,int ql,int qr,int l,int r)
{
if(l==r)
{
if(w[pr]-w[pl]==w[qr]-w[ql]) return 0;
return (w[pr]-w[pl]<w[qr]-w[ql]?-1:1);
}
if((w[ch[pr][0]]-w[ch[pl][0]]+mod)%mod==(w[ch[qr][0]]-w[ch[ql][0]]+mod)%mod)
return compare(ch[pl][1],ch[pr][1],ch[ql][1],ch[qr][1],mid+1,r);
else
return compare(ch[pl][0],ch[pr][0],ch[ql][0],ch[qr][0],l,mid);
}
}T2;
struct node
{
int l,r;
bool operator <(node b)
{
int tmp=T2.compare(T2.rt[l-1],T2.rt[r],T2.rt[b.l-1],T2.rt[b.r],1,m);
if(tmp==0) return l>b.l;
return tmp<0;
}
}ans[N];
struct tree1
{
int lmi[N<<2],lmx[N<<2],rmi[N<<2],rmx[N<<2];
ll sl[N<<2],sr[N<<2],sx[N<<2];
void update(int v)
{
lmi[v]=min(lmi[v<<1],lmi[v<<1|1]);
lmx[v]=max(lmx[v<<1],lmx[v<<1|1]);
rmi[v]=min(rmi[v<<1],rmi[v<<1|1]);
rmx[v]=max(rmx[v<<1],rmx[v<<1|1]);
sl[v]=sl[v<<1]+sl[v<<1|1];
sr[v]=sr[v<<1]+sr[v<<1|1];
sx[v]=sx[v<<1]+sx[v<<1|1];
}
void pushdown(int v)
{
if(lmi[v]>lmi[v<<1])
sl[v<<1]=sx[v<<1]*lmi[v],lmi[v<<1]=lmx[v<<1]=lmi[v];
if(lmi[v]>lmi[v<<1|1])
sl[v<<1|1]=sx[v<<1|1]*lmi[v],lmi[v<<1|1]=lmx[v<<1|1]=lmi[v];
if(rmx[v]<rmx[v<<1])
sr[v<<1]=sx[v<<1]*rmi[v],rmi[v<<1]=rmx[v<<1]=rmx[v];
if(rmx[v]<rmx[v<<1|1])
sr[v<<1|1]=sx[v<<1|1]*rmi[v],rmi[v<<1|1]=rmx[v<<1|1]=rmx[v];
}
void build(int v,int l,int r)
{
if(l==r)
{
lmi[v]=lmx[v]=sl[v]=l;
rmi[v]=rmx[v]=sr[v]=n;
sx[v]=1;
return ;
}
build(v<<1,l,mid);
build(v<<1|1,mid+1,r);
update(v);
}
ll calc(int v,int l,int r,int lx,int rx,int x)
{
if(lx>rx||!sx[v]) return 0;
if(l==lx&&r==rx)
{
if(lmi[v]>x) return 0;
if(rmx[v]<=x) return sr[v]-sl[v]+sx[v];
if(lmx[v]<=x&&rmi[v]>x) {return sx[v]*(x+1)-sl[v];}
}
pushdown(v);
if(rx<=mid) return calc(v<<1,l,mid,lx,rx,x);
if(lx>mid) return calc(v<<1|1,mid+1,r,lx,rx,x);
return calc(v<<1,l,mid,lx,mid,x)+calc(v<<1|1,mid+1,r,mid+1,rx,x);
}
void mdf(int v,int l,int r,int lx,int rx,int lc,int rc)
{
if(lx>rx||!sx[v]) return ;
if(l==lx&&r==rx)
{
bool flag=1;
if(lc>rmx[v]||rc<lmi[v])
{sx[v]=sl[v]=sr[v]=0;return ;}
else if(lc>=lmx[v]&&rc<=rmi[v])
sl[v]=sx[v]*lc,sr[v]=sx[v]*rc;
else if(lc<=lmi[v]&&rc>=lmx[v]&&rc<=rmi[v])
sr[v]=sx[v]*rc;
else if(lc>=lmx[v]&&lc<=rmi[v]&&rc>=rmx[v])
sl[v]=sx[v]*lc;
else if(lc<=lmi[v]&&rc>=rmx[v])
{}
else flag=0;
if(flag)
{
lmi[v]=max(lmi[v],lc);
lmx[v]=max(lmx[v],lc);
rmi[v]=min(rmi[v],rc);
rmx[v]=min(rmx[v],rc);
return ;
}
}
pushdown(v);
if(rx<=mid) mdf(v<<1,l,mid,lx,rx,lc,rc);
else if(lx>mid) mdf(v<<1|1,mid+1,r,lx,rx,lc,rc);
else mdf(v<<1,l,mid,lx,mid,lc,rc),mdf(v<<1|1,mid+1,r,mid+1,rx,lc,rc);
update(v);
}
node find(int v,int l,int r,ll k)
{
if(l==r) return (node){l,lmi[v]};
pushdown(v);
if(k<=sx[v<<1|1]) return find(v<<1|1,mid+1,r,k);
else return find(v<<1,l,mid,k-sx[v<<1|1]);
}
}T1;
ll check(int c,int x)
{
ll re=0;int sz=t[c].size();
for(int i=1;i<sz-x;i++)
re+=T1.calc(1,1,n,t[c][i-1]+1,min(t[c][i],n),t[c][i+x]-1);
re+=T1.calc(1,1,n,t[c][sz-x-1]+1,n,n);
return re;
}
node solve0()
{
for(int i=1;i<=m;i++)
{
int sz=t[i].size(),L=0,R=sz-2;
while(L<R)
{
int Mid=(L+R>>1);
if(check(i,Mid)<k0) L=Mid+1;
else R=Mid;
}
if(L) k0-=check(i,L-1);
for(int j=1;j<sz-L;j++)
T1.mdf(1,1,n,t[i][j-1]+1,min(t[i][j],n),t[i][j+L-1],t[i][j+L]-1);
T1.mdf(1,1,n,t[i][sz-L-1]+1,n,n+1,n+1);
}
return T1.find(1,1,n,k0);
}
struct gcmp
{
bool operator() (node a,node b)
{
return b<a;
}
};
priority_queue<node,vector<node>,gcmp > Q;
void solve1()
{
T2.rt[0]=0;
for(int i=1;i<=n;i++)
T2.rt[i]=++T2.cnt,T2.build(T2.cnt,T2.rt[i-1],1,m,a[i]);
for(int i=1;i<=n;i++)
{
int L=i,R=n;
while(L<R)
{
int Mid=(L+R>>1);
node p=(node){i,Mid};
if(p<ans[1]) L=Mid+1;
else R=Mid;
}
node q=(node){i,L};
if(!(q<ans[1])) Q.push(q);
}
for(int i=1;i<=k1;i++)
{
ans[i]=Q.top();Q.pop();
if(ans[i].r!=n) Q.push((node){ans[i].l,ans[i].r+1});
}
}
int main()
{
n=read();
scanf("%lld%lld",&k0,&k1);
k0=(ll)n*(n+1)/2-k0+1;
k1=(ll)n*(n+1)/2-k1+1;
swap(k0,k1);k1-=(k0-1);
for(int i=1;i<=n;i++)
z[i]=a[i]=read();
sort(z+1,z+n+1);
m=unique(z+1,z+n+1)-z-1;
for(int i=1;i<=m;i++)
t[i].push_back(0);
for(int i=1;i<=n;i++)
a[i]=lower_bound(z+1,z+m+1,a[i])-z,t[a[i]].push_back(i);
for(int i=1;i<=m;i++)
t[i].push_back(n+1);
T1.build(1,1,n);
ans[1]=solve0();
pw[0]=1;
for(int i=1;i<=m;i++)
pw[i]=pw[i-1]*W%mod;
solve1();
for(int i=k1;i;i--)
printf("%d %d\n",ans[i].l,ans[i].r);
return 0;
}