二分+主席树
个人感觉这道题非常好,思维难度高,代码好写(然而自己写的时候神志不清,转态全无qwq)
首先要知道一个关于中位数的基本套路,将大于等于它的赋值成1,小于它的赋值成-1
然后通过区间和就能找出中位数了
对于本题来说:
首先显然答案满足单调性,因为二分的是第k大的数,所以1,-1这个序列和肯定是单调变化的
其次我们考虐将[a,b],[c,d]区间转化成[a,b],(b,c),[c,d]
所以我们只需查询[a,b]中的最大右连续子段和(必选一个),(b,c)的区间和,[c,d]的最大左连续子段和(必选一个)
若这三个和>=0,则说明当前二分的第k大的数是ok的,否则不行,然后继续二分
对于维护每个数作为中位数时的1,-1序列,我们用主席树维护一下就ok了
效率:O(n(logn)^3)
/**************************************************************
Problem: 2653
User: syh0313
Language: C++
Result: Accepted
Time:2156 ms
Memory:12788 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <map>
#define lch a[n].lc
#define rch a[n].rc
using namespace std;
const int maxn=20010;
int n,m,v[maxn],vv[maxn],root[maxn],linum,line[maxn],topt,lim,t;
int cnt,to[maxn],st[maxn],nt[maxn],q[5],ans;
struct da{int lc,rc,sum,suml,sumr,l,r;}a[maxn*20];
map<int,int>lc;
void add(int x,int y)
{to[++cnt]=y; nt[cnt]=st[x]; st[x]=cnt;}
void updata(int n)
{
a[n].sum=a[lch].sum+a[rch].sum;
a[n].suml=max(a[lch].suml,a[lch].sum+a[rch].suml);
a[n].sumr=max(a[rch].sumr,a[rch].sum+a[lch].sumr);
}
void build_tree(int &n,int l,int r)
{
n=++topt; a[n].l=l; a[n].r=r;
if (l==r)
{
a[n].sum=a[n].suml=a[n].sumr=1;
return;
}
int mid=(l+r)>>1;
build_tree(lch,l,mid); build_tree(rch,mid+1,r);
updata(n);
}
void tree_add(int old,int &n,int l,int r,int lc)
{
if (n<=lim) n=++topt;
if (l==r) {a[n].sum=a[n].suml=a[n].sumr=-1; return;}
int mid=(l+r)>>1;
if (lc<=mid)
{
if (rch<=lim) rch=a[old].rc;
tree_add(a[old].lc,lch,l,mid,lc);
}
else
{
if (lch<=lim) lch=a[old].lc;
tree_add(a[old].rc,rch,mid+1,r,lc);
}
updata(n);
}
int qsum(int n,int L,int R,int l,int r)
{
if (l>r) return 0;
if (L==l && R==r) return a[n].sum;
int mid=(L+R)>>1;
if (r<=mid) return qsum(lch,L,mid,l,r);
else if (l>=mid+1) return qsum(rch,mid+1,R,l,r);
else return qsum(lch,L,mid,l,mid)+qsum(rch,mid+1,R,mid+1,r);
}
int qsuml(int n,int L,int R,int l,int r,int k)
{
if (L==l && R==r) return a[n].suml;
int mid=(L+R)>>1;
if (r<=mid) return qsuml(lch,L,mid,l,r,k);
else if (l>=mid+1) return qsuml(rch,mid+1,R,l,r,k);
else
{
int kk=qsum(root[k],0,t,l,mid)+qsuml(rch,mid+1,R,mid+1,r,k);
return max(kk,qsuml(lch,L,mid,l,mid,k));
}
}
int qsumr(int n,int L,int R,int l,int r,int k)
{
if (L==l && R==r) return a[n].sumr;
int mid=(L+R)>>1;
if (r<=mid) return qsumr(lch,L,mid,l,r,k);
else if (l>=mid+1) return qsumr(rch,mid+1,R,l,r,k);
else
{
int kk=qsum(root[k],0,t,mid+1,r)+qsumr(lch,L,mid,l,mid,k);
return max(kk,qsumr(rch,mid+1,R,mid+1,r,k));
}
}
int main()
{
scanf("%d",&n); t=n-1;
for (int i=0;i<n;i++) scanf("%d",&v[i]),vv[i]=v[i];
sort(vv,vv+n);
for (int i=0;i<n;i++) if (!lc[vv[i]]) lc[vv[i]]=++linum,line[linum]=vv[i];
for (int i=0;i<n;i++) add(lc[v[i]],i);
build_tree(root[1],0,n-1);
for (int i=2;i<=linum;i++)
{
int p=st[i-1]; lim=topt;
while (p) {tree_add(root[i-1],root[i],0,n-1,to[p]); p=nt[p];}
}
scanf("%d",&m);
while (m--)
{
for (int i=1;i<=4;i++) scanf("%d",&q[i]),q[i]=(q[i]+ans)%n;
sort(q+1,q+5); int l=1,r=linum; ans=0;
while (l<=r)
{
int mid=(l+r)>>1;
int ss=qsum(root[mid],0,n-1,q[2]+1,q[3]-1);
ss+=qsumr(root[mid],0,n-1,q[1],q[2],mid);
ss+=qsuml(root[mid],0,n-1,q[3],q[4],mid);
if (ss>=0) {ans=max(ans,mid); l=mid+1;}else r=mid-1;
}
ans=line[ans];
printf("%d\n",ans);
}
return 0;
}
1757

被折叠的 条评论
为什么被折叠?



