[国家集训队]middle

题目描述
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。
位置也从0开始标号。
我会使用一些方式强制你在线。
输入输出格式
输入格式:
第一行序列长度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]。

输入保证满足条件。
输出格式:
Q行依次给出询问的答案。

输入输出样例


输入样例#1: 
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
输出样例#1: 
271451044
271451044
969056313
说明

 暴力过不了


题意还是比较好理解的

嗯哼,现在让我们进入正题。

做完这题超爽

好吧,还是让我写一下题意

题意:给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。 强制在线。


这一题对于一个我这个刚学主席树不到两天的人简直就是作死。


思路:首先考虑二分答案,判断可行的方法则是看是否大于他的数可以比小于他的数多。则考虑对于[b, c]直接求出来(因为不管怎么取都会取到这一段),对于[a, b-1]求最大的后缀(带有一点贪心思想),[c+1, d]求最大前缀,可以建线段树维护。如果对于每一个数建一个线段树,空间(嘿嘿嘿)就会爆了,所以我们考虑使用主席树维护。

怎么判断大于他的数可以比小于他的数多呢?

这里我们可以用一种比较巧妙的方法,我们把小于当前数的数全部标-1,大于等于的标1,到时只需要维护这个数组就可以了(是不是很棒棒)(手动滑稽)

总的复杂度 O(可以过(手动划去)显然可以(碾过)解决这道题。                                                      

下面请出我奇丑无比的代码:

#include<bits/stdc++.h>
using namespace std;
const int N=10000007;
int a[N],ch[N][2],b[N],c[N],tot,n,m,root[N];
struct A{
    int id,val;
};
void push(int rt){
    a[rt]=a[ch[rt][0]]+a[ch[rt][1]];
    b[rt]=max(b[ch[rt][0]]+a[ch[rt][1]],b[ch[rt][1]]);     //最大后缀 
    c[rt]=max(c[ch[rt][0]],c[ch[rt][1]]+a[ch[rt][0]]); 	   //最大前缀 
}
void build(int l,int r,int rt){    //奇奇gay gay 的建树
    if(l==r){
    	a[rt]=b[rt]=c[rt]=1;
   		 return;	
    }
    int mid=(l+r)>>1;
    tot++;
    ch[rt][0]=tot;
    build(l,mid,tot);
    tot++;
    ch[rt][1]=tot;
    build(mid+1,r,tot);
    push(rt);
}
void insert(int l,int r,int x,int rt){ //奇奇gay gay 的插入♂
    if(l==r){
        tot++;
        a[tot]=b[tot]=c[tot]=-1;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=x){
        insert(l,mid,x,ch[rt][0]);
        tot++;
        ch[tot][0]=tot-1;
        ch[tot][1]=ch[rt][1];
        push(tot);
    }else{
        insert(mid+1,r,x,ch[rt][1]);
        tot++;
        ch[tot][1]=tot-1;
        ch[tot][0]=ch[rt][0];
        push(tot);
    }
}
int q[N],qq[N];
A p[N];
int query(int l,int r,int L,int R,int rt){ //区间和 
    if(L<=l&&r<=R) return a[rt];
    int mid=(l+r)>>1,ret=0;
    if(L<=mid)  ret+=query(l,mid,L,R,ch[rt][0]);
    if(R>mid) ret+=query(mid+1,r,L,R,ch[rt][1]);
    return ret;
}

int query1(int l,int r,int L,int R,int rt){ //最大后缀 
    if(L>R) return 0;
    if(L<=l&&r<=R) return b[rt];
    int mid=(l+r)>>1,ret=0;
    if(L<=mid)  ret=query1(l,mid,L,R,ch[rt][0]);
    if(R>mid) ret=max(ret+query(mid+1,r,L,R,ch[rt][1]),query1(mid+1,r,L,R,ch[rt][1]));
    return ret;
}
int query2(int l,int r,int L,int R,int rt){  //最大前缀 
    if(L>R) return 0;
    if(L<=l&&r<=R) return c[rt];
    int mid=(l+r)>>1,ret=0;
    if(R>mid)  ret=query2(mid+1,r,L,R,ch[rt][1]);
    if(L<=mid) ret=max(ret+query(l,mid,L,R,ch[rt][0]),query2(l,mid,L,R,ch[rt][0]));
    return ret;
}
int check(int l,int r,int ll,int rr){//毁天灭地的二分答案 
    int L=1,R=n+1,mid;
    while(L+1<R){
        mid=(L+R)>>1;
        if(max(query1(1,n,l,r-1,root[mid-1]),0)+max(query2(1,n,ll+1,rr,root[mid-1]),0)+query(1,n,r,ll,root[mid-1])>=0)
        L=mid; else R=mid; 
    }
    return L;
}
bool cmp(A x,A y){
    return x.val<y.val;
}
int k,l,r,ll,rr,pp[5];
vector<int>pq[1000005];
int main(){ 
    cin>>n;
    root[0]=0;
    build(1,n,root[0]);
    for(int i=1;i<=n;i++)  scanf("%d",&p[i].val),q[i]=p[i].val,qq[i]=p[i].val,p[i].id=i; //中二的离散化
    sort(p+1,p+1+n,cmp);
    for(int i=1;i<=n;i++){
        insert(1,n,p[i].id,root[i-1]),root[i]=tot;
    }
    int last=0;
    cin>>m;
    while(m--){
        scanf("%d%d%d%d",&l,&r,&ll,&rr);
        pp[0]=(l+last)%n;pp[1]=(r+last)%n;pp[2]=(ll+last)%n;pp[3]=(rr+last)%n;
        sort(pp,pp+4);
        last=check(pp[0]+1,pp[1]+1,pp[2]+1,pp[3]+1);
        last=p[last].val;
        printf("%d\n",last);
        last%=n;
    }
}//一百零八行的结束(拜拜~)


猜你喜欢

转载自blog.csdn.net/qq_38944163/article/details/80418512