• [二分][主席树] Luogu P2839 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

    说明

    0:n,Q<=100

    1,...,5:n<=2000

    0,...,19:n<=20000,Q<=25000

    题解

    • 考虑二分答案,那么当前mid是合法的情况就是存在一个序列,比它大的个数等于比它小的个数
    • 那么我们就可以把比它大的数看作是1,比它小的就是-1,一个合法序列的和>=0
    • 那么就是相当于求在左端点在[a,b]中右端点在[c,d]中的最大子段和
    • 这个东西可以拆分成[a~b]的最大后缀和,[b+1~c-1]的和,和[c~d]最大前缀和
    • 怎么维护这个东东呢,用线段树的话空间会爆炸,那我们就可以打主席树动态开点

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define N 20010
     6 using namespace std;
     7 struct note{int v,d;}a[N];
     8 struct node{int s,ls,rs,l,r;}t[N*30];
     9 int ans,tot,n,m,root[N],q[4];
    10 bool cmp(note a,note b) { return a.v<b.v; }
    11 node merge(node y,node z) 
    12 {
    13     node x;
    14     x.s=y.s+z.s,x.ls=max(y.ls,y.s+z.ls),x.rs=max(z.rs,z.s+y.rs);
    15     return x;
    16 }
    17 void pushup(int d)
    18 {
    19     int l=t[d].l,r=t[d].r;
    20     t[d].s=t[l].s+t[r].s,t[d].ls=max(t[l].ls,t[l].s+t[r].ls),t[d].rs=max(t[r].rs,t[l].rs+t[r].s);
    21 }
    22 void build(int &d,int l,int r)
    23 {
    24     d=++tot;
    25     if (l==r) { t[d].s=t[d].ls=t[d].rs=1; return; }
    26     int mid=l+r>>1;
    27     build(t[d].l,l,mid),build(t[d].r,mid+1,r),pushup(d);
    28 }
    29 void change(int &d,int l,int r,int x)
    30 {
    31     t[++tot]=t[d],d=tot;
    32     if (l==r) { t[d].s=t[d].ls=t[d].rs=-1; return; }
    33     int mid=l+r>>1;
    34     if (x<=mid) change(t[d].l,l,mid,x); else change(t[d].r,mid+1,r,x); pushup(d);
    35 }
    36 node find(int d,int l,int r,int x,int y)
    37 {
    38     if (x>y) return t[0];
    39     if (l==x&&r==y) return t[d];
    40     int mid=l+r>>1;
    41     if (y<=mid) return find(t[d].l,l,mid,x,y);
    42     else if (x>mid) return find(t[d].r,mid+1,r,x,y);
    43     else return merge(find(t[d].l,l,mid,x,mid),find(t[d].r,mid+1,r,mid+1,y));
    44 }
    45 bool check(int x) { return find(root[x],1,n,q[0],q[1]).rs+find(root[x],1,n,q[1]+1,q[2]-1).s+find(root[x],1,n,q[2],q[3]).ls>=0; }
    46 int main()
    47 {
    48     scanf("%d",&n);
    49     for (int i=1;i<=n;i++) scanf("%d",&a[i].v),a[i].d=i;
    50     sort(a+1,a+n+1,cmp),build(root[1],1,n);
    51     for (int i=2;i<=n;i++) root[i]=root[i-1],change(root[i],1,n,a[i-1].d);
    52     for (scanf("%d",&m);m;m--)
    53     {
    54         for (int i=0,x;i<4;i++) scanf("%d",&x),q[i]=(x+ans)%n+1;
    55         sort(q,q+4); int l=1,r=n+1,mid;
    56         while (l<r)
    57         {
    58             int mid=l+r>>1;
    59             if (check(mid)) l=mid+1; else r=mid; 
    60         }
    61         printf("%d
    ",ans=a[l-1].v);
    62     }
    63 }
  • 相关阅读:
    BZOJ1036 [ZJOI2008]树的统计Count
    3224: Tyvj 1728 普通平衡树
    BZOJ 3343教主的魔法
    BZOJ 2002[Hnoi2010]Bounce 弹飞绵羊
    BZOJ1503 [NOI2004]郁闷的出纳员
    BZOJ1588 [HNOI2002]营业额统计
    带有上下界的网络流
    堆优化 dijkstra +路径输出
    luogu P3388 【模板】割点(割顶)
    Tarjan 算法求无向图的割顶和桥
  • 原文地址:https://www.cnblogs.com/Comfortable/p/11207442.html
Copyright © 2020-2023  润新知