题目描述
分析
这道题这么多关键字,似乎很难搞,先想一想。
30分暴力很容易嘛,就是莫队之后上KD树二位查询。
100分呢?
考虑到最后一维的经典做法是主席树上二分,我们考虑整体二分来做。
首先为了方便,我们先把询问的x,y处理一下,把排名在[x,y]之间变成第一关键字在[x’,y’]之间。
考虑整体二分。先考虑一开始对询问区间[1,m]怎么做。我们有二分的答案mid=(1+n)/2,考虑先排序去掉一维的限制,我们把询问拆成l-1,r两个询问,和n个元素扔到一起,按照序列下标排序,用数据结构维护。那么现在询问就变成了,查询[x,y]之间有几个元素第二关键字小于等于mid。由于整体二分,第二关键字大于mid的元素不用管,那么先把他扔出处理队列,不管他;小于等于mid的元素在当前局面下性质是一样的,所以第二关键字也不用管了。现在只要处理单点插入1,区间查询总和这一问题,那么可以用树状数组做,常数小,log2下时间有保证。
好了处理完之后,我们要给询问和元素分类,然后分治下去了。
考虑一个询问,如果区间查询出来的结果cnt大于它的k,把他扔到mid+1~r的分治区间,并且把k-=cnt;不然就直接扔进1~mid的分治区间,k不动。
对于一个元素,如果它的第二关键字大于mid,他对左边的1~mid的分治区间没有任何贡献,扔到右边;如果第一关键字小于等于mid,他对右边的分治区间已经计算过贡献,即前面的k-=cnt,所以没必要传递到右区间,直接扔进左区间。
这样就做完了,时间复杂度O(nlogn+nlog2n),一个主席树,一个整体二分+树状数组。
60分也很简单,你把树状数组改成线段树就行了。
我就是打了线段树然后开了O2。
代码
#pragma GCC optimize(2)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int N=1e5+5,mo=1e9+7,mx=1e9;
int tr[N*4];
struct Query
{
int l,r,x,y,k,s,id;
}q[N*3],p[N*3];
int tq;
struct rec
{
int pos,x,y,s,id;
}c[N*3];
int tc;
bool operator <(rec a,rec b)
{
return a.pos<b.pos||a.pos==b.pos&&a.s<b.s;
}
int n,m,i,j,kan,a[N],b[N],prt[N],tmp,ax,ay,cnt[N];
int ftr[N*8],ls[N*8],rs[N*8],tt,rt[N];
void changed(int ax,int x,int l,int r,int pos)
{
if (l==r)
{
ftr[x]=ftr[ax]+1;
return ;
}
int m=(l+r)/2;
if (pos<=m)
{
rs[x]=rs[ax];ls[x]=++tt;
changed(ls[ax],ls[x],l,m,pos);
}else
{
ls[x]=ls[ax];rs[x]=++tt;
changed(rs[ax],rs[x],m+1,r,pos);
}
ftr[x]=ftr[ls[x]]+ftr[rs[x]];
}
int got(int ax,int x,int l,int r,int k)
{
if (l==r) return l;
int m=(l+r)/2,temp=ftr[ls[x]]-ftr[ls[ax]];
if (temp>=k)
return got(ls[ax],ls[x],l,m,k);else
return got(rs[ax],rs[x],m+1,r,k-temp);
}
void predo()
{
fo(i,1,n)
{
rt[i]=++tt;
changed(rt[i-1],rt[i],1,n,a[i]);
}
fo(i,1,m)
{
ax=got(rt[q[i].l-1],rt[q[i].r],1,n,q[i].x);
q[i].x=ax;
ay=got(rt[q[i].l-1],rt[q[i].r],1,n,q[i].y);
q[i].y=ay;
}
}
void change(int x,int l,int r,int pos,int inc)
{
if (l==r)
{
tr[x]+=inc;
return ;
}
int m=(l+r)/2;
if (pos<=m)
change(x*2,l,m,pos,inc);else
change(x*2+1,m+1,r,pos,inc);
tr[x]=tr[x*2]+tr[x*2+1];
}
int get(int x,int l,int r,int i,int j)
{
if (l==i&&r==j) return tr[x];
int m=(l+r)/2;
if (j<=m)
return get(x*2,l,m,i,j);else
if (m<i)
return get(x*2+1,m+1,r,i,j);else
return get(x*2,l,m,i,m)+get(x*2+1,m+1,r,m+1,j);
}
void solve(int sig,Query *que,int st,int en,int l,int r)
{
if (st>en) return ;
if (l==r)
{
fo(i,st,en) if (que[i].s)
prt[que[i].id]=l;
return ;
}
int mid=(l+r)/2,i;
tc=0;
fo(i,st,en)
{
if (!que[i].s&&que[i].y<=mid)
{
c[++tc].pos=que[i].l;
c[tc].x=que[i].x;
c[tc].s=-2;
}
if (que[i].s)
{
c[++tc].pos=que[i].l-1;
c[tc].x=que[i].x;
c[tc].y=que[i].y;
c[tc].s=-1;
c[tc].id=i;
c[++tc].pos=que[i].r;
c[tc].x=que[i].x;
c[tc].y=que[i].y;
c[tc].s=1;
c[tc].id=i;
}
cnt[i]=0;
}
sort(c+1,c+1+tc);
fo(i,1,tc)//process
{
if (c[i].s==-2) change(1,1,n,c[i].x,1);
else
{
tmp=get(1,1,n,c[i].x,c[i].y);
cnt[c[i].id]+=tmp*c[i].s;
}
}
fo(i,1,tc) if (c[i].s==-2) change(1,1,n,c[i].x,-1);// delete
int sten=st-1,enst=en+1;
Query *nq;
if (sig) nq=p;else nq=q;
fo(i,st,en)
{
if ((!que[i].s&&que[i].y<=mid)||(que[i].s&&cnt[i]>=que[i].k))
{
sten++;
nq[sten]=que[i];
}else
{
enst--;
nq[enst]=que[i];
if (que[i].s) nq[enst].k-=cnt[i];
}
}
solve(sig^1,nq,st,sten,l,mid);
solve(sig^1,nq,enst,en,mid+1,r);
}
int main()
{
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) scanf("%d",a+i);
fo(i,1,n) scanf("%d",b+i);
scanf("%d\n",&m);
fo(i,1,m)
{
scanf("%d %d %d %d %d",&q[i].l,&q[i].r,&q[i].x,&q[i].y,&q[i].k);
q[i].s=1;
q[i].id=i;
}
predo();
tq=m;
fo(i,1,n)
{
tq++;
q[tq].s=0;
q[tq].l=q[tq].r=i;
q[tq].x=a[i];
q[tq].y=b[i];
}
solve(1,q,1,tq,1,n);
fo(i,1,m) printf("%d\n",prt[i]);
}