[BZOJ]2653: middle
题解:神.....神题
求中位数->区间第k大->主席树 好了进死胡同了 因为没办法差分区间和区间
我们再考虑 对于中位数...那我们把大于等于这个数的置1 小于这个数置-1 然后查询区间和是否大于等于0来check 根据这个性质 我们考虑二分中位数(因为具有单调性...很明显证明
但是每次二分都去修改序列 复杂度太高接受不了 我们考虑用主席树优化 记录把某个位置置为-1后维护各个历史版本的信息 然后类似于区间合并 维护各个版本情况下 从区间左端点最大连续和 右端点最大连续和 然后check即可
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
const int MAXN=3e5+10;
const double eps=1e-8;
#define ll long long
using namespace std;
struct edge{int t,v;edge*next;}e[MAXN<<1],*h[MAXN],*o=e;
void add(int x,int y,int vul){o->t=y;o->v=vul;o->next=h[x];h[x]=o++;}
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
typedef struct node{
int l,r,lnum,rnum,sum;
}node;
node d[MAXN*21];int cnt;
int rt[MAXN],a[MAXN];
void up(int x){
d[x].sum=d[d[x].l].sum+d[d[x].r].sum;
d[x].lnum=max(d[d[x].l].lnum,d[d[x].l].sum+d[d[x].r].lnum);
d[x].rnum=max(d[d[x].r].rnum,d[d[x].r].sum+d[d[x].l].rnum);
}
void built(int &x,int l,int r){
x=++cnt;
if(l==r){d[x].lnum=d[x].rnum=d[x].sum=1;return ;}
int mid=(l+r)>>1;
built(d[x].l,l,mid);
built(d[x].r,mid+1,r);
up(x);
}
void update(int &x,int y,int l,int r,int t){
x=++cnt;d[x]=d[y];
if(l==r){d[x].lnum=d[x].rnum=0;d[x].sum=-1;return ;}
int mid=(l+r)>>1;
if(t<=mid)update(d[x].l,d[y].l,l,mid,t);
else update(d[x].r,d[y].r,mid+1,r,t);
up(x);
}
int ans1;
void query1(int x,int l,int r,int ql,int qr){
if(!x)return ;
if(ql<=l&&r<=qr){ans1+=d[x].sum;return ;}
int mid=(l+r)>>1;
if(ql<=mid)query1(d[x].l,l,mid,ql,qr);
if(qr>mid)query1(d[x].r,mid+1,r,ql,qr);
}
node ans2;
void query2(int x,int l,int r,int ql,int qr){
if(!x)return ;
if(ql<=l&&r<=qr){
ans2.lnum=max(ans2.lnum,ans2.sum+d[x].lnum);
ans2.rnum=max(d[x].rnum,d[x].sum+ans2.rnum);
ans2.sum+=d[x].sum;
return ;
}
int mid=(l+r)>>1;
if(ql<=mid)query2(d[x].l,l,mid,ql,qr);
if(qr>mid)query2(d[x].r,mid+1,r,ql,qr);
}
typedef struct Node{
int id,k;
friend bool operator<(Node aa,Node bb){return aa.k<bb.k;}
}Node;
Node que[MAXN];
int q[4],n;
bool check(int t){
//cout<<t<<"===="<<endl;
t--;
ans1=0;query1(rt[t],1,n,q[1],q[2]);
// cout<<ans1<<endl;
ans2.lnum=ans2.rnum=ans2.sum=0;
query2(rt[t],1,n,q[0],q[1]-1);ans1+=ans2.rnum;
//cout<<ans2.lnum<<" "<<ans2.rnum<<" "<<ans2.sum<<endl;
ans2.lnum=ans2.rnum=ans2.sum=0;
query2(rt[t],1,n,q[2]+1,q[3]);ans1+=ans2.lnum;
//cout<<ans2.lnum<<" "<<ans2.rnum<<" "<<ans2.sum<<endl;
//cout<<ans1<<endl;
if(ans1>=0)return 1;
return 0;
}
int main(){
n=read();
inc(i,1,n)que[i].k=read(),que[i].id=i;
sort(que+1,que+n+1);
built(rt[0],1,n);
inc(i,1,n)update(rt[i],rt[i-1],1,n,que[i].id);
int lastAns=0;
int m=read();
while(m--){
inc(i,0,3)q[i]=(read()+lastAns)%n+1;
sort(q,q+4);
//inc(i,0,3)cout<<q[i]<<" ";
//cout<<endl;
int l=1;int r=n;int ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))ans=mid,l=mid+1;
else r=mid-1;
}
lastAns=que[ans].k;
printf("%d\n",lastAns);
}
return 0;
}
2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2721 Solved: 1547
[Submit][Status][Discuss]
Description
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。
Input
第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是
x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的
要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
第一行所谓“排过序”指的是从小到大排序!
n<=20000,Q<=25000
Output
Q行依次给出询问的答案。
Sample Input
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
271451044
969056313

浙公网安备 33010602011771号