zoukankan      html  css  js  c++  java
  • bzoj 2653: middle

    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]。  
    输入保证满足条件。
    第一行所谓“排过序”指的是从大到小排序!

    Output

    Q行依次给出询问的答案。

    Sample Input

    5
    170337785
    271451044
    22430280
    969056313
    206452321
    3
    3 1 0 2
    2 3 1 4
    3 1 4 0

    271451044
    271451044
    969056313

    Sample Output

    HINT 

    0:n,Q<=100

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

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

    Source

    陈老师的题,然而大佬们都是随意切。。。

    首先记住中位数的套路,二分答案然后>=mid的为1,<mid的为-1;

    那么check的话我们相当于是要找一个左端点在[a,b],右端点在[c,d]且子段和>=0的子段。。。

    我一开始想了一个傻逼东西,每个点维护sum[i]表示1-i的前缀和,然后查询[c,d]中的sum_max,查询[a,b]中的sum_min;

    然后前缀和相减算最大子段和,即sum_max-sum_min。。。

    但这个东西显然只能处理一次询问,因为每次二分后都需要O(n)的时间来重构sum[i],显然做不了很多次询问。。。

    考虑换一种做法,把询问拆为三个:

    [a,b]的后缀最大连续值,[b+1,c-1]的子段和,[c,d]的前缀最大连续值,三个相加。。。

    这些东西都可以在线段树上维护。。。现在看来这样做每次的复杂度跟上面一种做法没区别。。。

    但是他可以加可持久化的特技。。。

    考虑把Mid改为Mid+1后,只需要把Mid的权值该为-1,而其他点的权值不需要改动(每次只有单点修改,而维护sum的话就GG)

    所以可以用可持久化的那一套理论,对于每一个Mid值都搞一棵线段树。。

    这样询问时就可以直接查询二分值版本的线段树,而不需要对于每个二分值重构线段树了。。

    具体做法,把Mid从小到大建线段树,我们以Mid的线段树为历史版本,然后Mid+1的线段树就用可持久化的理论,修改Mid线段树的一条链。。

    线段树上的节点维护三个东西(和,前缀最大值,后缀最大值)就好了。。。

    // MADE BY QT666
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<iostream>
    #include<cstring>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int N=500050;
    vector<int> p[N];
    int hsh[N],tot,sz,rt[N],ls[N*20],rs[N*20],sum[N*20],lmax[N*20],rmax[N*20],a[N];
    int n,Q,q[10];
    void pushup(int x){ 
      sum[x]=sum[ls[x]]+sum[rs[x]];
      lmax[x]=max(lmax[ls[x]],sum[ls[x]]+lmax[rs[x]]);
      rmax[x]=max(rmax[rs[x]],sum[rs[x]]+rmax[ls[x]]);
    }
    void insert(int x,int &y,int l,int r,int u,int v){
      y=++sz;ls[y]=ls[x],rs[y]=rs[x];
      if(l==r){
        sum[y]=lmax[y]=rmax[y]=v;return;
      }
      int mid=(l+r)>>1;
      if(u<=mid) insert(ls[x],ls[y],l,mid,u,v);
      else insert(rs[x],rs[y],mid+1,r,u,v);
      pushup(y);
    }
    int query_sum(int x,int l,int r,int xl,int xr){
      if(xl>xr) return 0;
      if(xl<=l&&r<=xr) return sum[x];
      int mid=(l+r)>>1;
      if(xr<=mid) return query_sum(ls[x],l,mid,xl,xr);
      else if(xl>mid) return query_sum(rs[x],mid+1,r,xl,xr);
      else return query_sum(ls[x],l,mid,xl,mid)+query_sum(rs[x],mid+1,r,mid+1,xr);
    }
    int query_left(int x,int l,int r,int xl,int xr){
      if(xl<=l&&r<=xr) return lmax[x];
      int mid=(l+r)>>1;
      if(xr<=mid) return query_left(ls[x],l,mid,xl,xr);
      else if(xl>mid) return query_left(rs[x],mid+1,r,xl,xr);
      else return max(query_left(ls[x],l,mid,xl,mid),query_sum(ls[x],l,mid,xl,mid)+query_left(rs[x],mid+1,r,mid+1,xr));
    }
    int query_right(int x,int l,int r,int xl,int xr){
      if(xl<=l&&r<=xr) return rmax[x];
      int mid=(l+r)>>1;
      if(xr<=mid) return query_right(ls[x],l,mid,xl,xr);
      else if(xl>mid) return query_right(rs[x],mid+1,r,xl,xr);
      else return max(query_right(ls[x],l,mid,xl,mid)+query_sum(rs[x],mid+1,r,mid+1,xr),query_right(rs[x],mid+1,r,mid+1,xr));
    }
    bool check(int mid,int a,int b,int c,int d){
      int part1=query_right(rt[mid],1,n,a,b);
      int part2=query_sum(rt[mid],1,n,b+1,c-1);
      int part3=query_left(rt[mid],1,n,c,d);
      return part1+part2+part3>=0;
    }
    int main(){
      scanf("%d",&n);
      for(int i=1;i<=n;i++) scanf("%d",&a[i]),hsh[++tot]=a[i];
      sort(hsh+1,hsh+1+tot);tot=unique(hsh+1,hsh+1+tot)-hsh-1;
      for(int i=1;i<=n;i++){
        a[i]=lower_bound(hsh+1,hsh+1+tot,a[i])-hsh,p[a[i]].push_back(i);
      }
      for(int i=1;i<=n;i++){
        if(a[i]>=1) insert(rt[1],rt[1],1,n,i,1);
        else insert(rt[1],rt[1],1,n,i,0);
      }
      for(int i=2;i<=tot;i++){
        rt[i]=rt[i-1];
        for(int j=0;j<p[i-1].size();j++){
          insert(rt[i],rt[i],1,n,p[i-1][j],-1);
        }
      }
      int ans=0;scanf("%d",&Q);
      for(int i=1;i<=Q;i++){
        int a,b,c,d;scanf("%d%d%d%d",&a,&b,&c,&d);
        q[1]=(a+ans)%n,q[2]=(b+ans)%n,q[3]=(c+ans)%n,q[4]=(d+ans)%n;
        sort(q+1,q+1+4);a=q[1]+1,b=q[2]+1,c=q[3]+1,d=q[4]+1;
        int l=1,r=tot;
        while(l<=r){
          int mid=(l+r)>>1;
          if(check(mid,a,b,c,d)) l=mid+1,ans=hsh[mid];
          else r=mid-1;
        }
        printf("%d
    ",ans);
      }
      return 0;
    }
  • 相关阅读:
    XJ20夏令营做题记录(长期更新)
    洛谷P6623——[省选联考 2020 A 卷] 树
    [游记] 2020ZJOI 爆零记
    CF1017G——The Tree
    CF715E—— Complete the Permutations
    学习笔记——树的初步整理
    学习笔记——DP初步整理
    洛谷P5290——春节十二响
    POJ3017——Cut the Sequence(单调队列+堆优化DP)
    Java控制整形输入的法子
  • 原文地址:https://www.cnblogs.com/qt666/p/7258732.html
Copyright © 2011-2022 走看看