[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