zoukankan      html  css  js  c++  java
  • [国家集训队]middle

    嘟嘟嘟


    有谁能想到这题会用到主席树呢?(不愧是WJMZBMR出的题)


    首先考虑如果区间是固定的话,中位数该怎么求。
    没错,二分。如果大于当前二分值(mid)的数比小于(mid)的数多,说明(mid)还可以再变大,向右二分;否则向左二分。
    如果我们把小于(mid)的数都标记成(-1),大于的标记成(1),那么只用判断这个区间的和是否(geqslant 0)就行了。


    但现在区间不固定。首先([b + 1, c - 1])是一定要选的。对于([a, b])([c, d]),因为要让中位数尽量大,所以应该选([a, b])的最大后缀和以及([c, d])的最大前缀和。


    主要思路就是这些,但单次查询的复杂度是(O(n log{n}))的,过不了。得想办法优化。


    如果对每一个二分的值建一棵区间线段树(这里的二分在序列中的值中进行就行,而不是(1)(1e9),所以只用建(n)棵),把小于他的都标记成(1),大于标记成(-1),那么每一次查询就能达到(O(log ^ 2{n}))了。
    但是很显然这样空间开不下,而且预处理复杂度过高。所以现在得想办法减少预处理的时间。
    如果把序列中的数排一个序,会发现对于相邻的两个不一样的数(因为数字可能有重),建的线段树只有一处不一样,而这一处不一样只会导致线段树中的一条链改变。所以我们只要单独把这条链提建出来就行了。
    然后就会发现这其实就是一棵主席树呀。


    于是这题就写完了。

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    #define enter puts("") 
    #define space putchar(' ')
    #define Mem(a, x) memset(a, x, sizeof(a))
    #define rg register
    typedef long long ll;
    typedef double db;
    const int INF = 0x3f3f3f3f;
    const db eps = 1e-8;
    const int maxn = 2e4 + 5;
    const int maxt = 2e6 + 5;
    inline ll read()
    {
      ll ans = 0;
      char ch = getchar(), last = ' ';
      while(!isdigit(ch)) {last = ch; ch = getchar();}
      while(isdigit(ch)) {ans = (ans << 1) + (ans << 3) + ch - '0'; ch = getchar();}
      if(last == '-') ans = -ans;
      return ans;
    }
    inline void write(ll x)
    {
      if(x < 0) x = -x, putchar('-');
      if(x >= 10) write(x / 10);
      putchar(x % 10 + '0');
    }
    
    int n, _n, m, a[maxn], b[maxn], q[4];
    vector<int> v[maxn];
    
    struct Tree
    {
      int ls, rs;
      int sum, lmax, rmax;
    }t[maxt];
    int root[maxn], cnt = 0;
    void pushup(int now)
    {
      t[now].sum = t[t[now].ls].sum + t[t[now].rs].sum;
      t[now].lmax = max(t[t[now].ls].lmax, t[t[now].ls].sum + t[t[now].rs].lmax);
      t[now].rmax = max(t[t[now].rs].rmax, t[t[now].rs].sum + t[t[now].ls].rmax);
    }
    void build(int& now, int l, int r)
    {
      if(!now) now = ++cnt;
      if(l == r) {t[now].sum = t[now].lmax = t[now].rmax = 1; return;}
      int mid = (l + r) >> 1;
      build(t[now].ls, l, mid);
      build(t[now].rs, mid + 1, r);
      pushup(now);
    }
    void insert(int old, int& now, int l, int r, int id)
    {
      t[now = ++cnt] = t[old];
      if(l == r) {t[now].sum = t[now].lmax = t[now].rmax = -1; return;}
      int mid = (l + r) >> 1;
      if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
      else insert(t[old].rs, t[now].rs, mid + 1, r, id);
      pushup(now);
    }
    int querySum(int now, int l, int r, int L, int R)
    {
      if(R < L) return 0;
      if(l == L && r == R) return t[now].sum;
      int mid = (l + r) >> 1;
      if(R <= mid) return querySum(t[now].ls, l, mid, L, R);
      else if(L > mid) return querySum(t[now].rs, mid + 1, r, L, R);
      else return querySum(t[now].ls, l, mid, L, mid) + querySum(t[now].rs, mid + 1, r, mid + 1, R);
    }
    int queryL(int now, int l, int r, int L, int R)
    {
      if(l == L && r == R) return t[now].lmax;
      int mid = (l + r) >> 1;
      if(R <= mid) return queryL(t[now].ls, l, mid, L, R);
      else if(L > mid) return queryL(t[now].rs, mid + 1, r, L, R);
      else
        {
          int ret1 = queryL(t[now].ls, l, mid, L, mid);
          int ret2 = querySum(t[now].ls, l, mid, L, mid) + queryL(t[now].rs, mid + 1, r, mid + 1, R);
          return max(ret1, ret2);
        }
    }
    int queryR(int now, int l, int r, int L, int R)
    {
      if(l == L && r == R) return t[now].rmax;
      int mid = (l + r) >> 1;
      if(R <= mid) return queryR(t[now].ls, l, mid, L, R);
      else if(L > mid) return queryR(t[now].rs, mid + 1, r, L, R);
      else
        {
          int ret1 = queryR(t[now].rs, mid + 1, r, mid + 1, R);
          int ret2 = querySum(t[now].rs, mid + 1, r, mid + 1, R) + queryR(t[now].ls, l, mid, L, mid);
          return max(ret1, ret2);
        }
    }
    
    bool judge(int x)
    {
      int ans1 = querySum(root[x], 1, n, q[1] + 1, q[2] - 1);
      int ans2 = queryR(root[x], 1, n, q[0], q[1]);
      int ans3 = queryL(root[x], 1, n, q[2], q[3]);
      return ans1 + ans2 + ans3 >= 0;
    }
    int solve()
    {
      int L = 1, R = _n;
      while(L < R)
        {
          int mid = (L + R + 1) >> 1;
          if(judge(mid)) L = mid;
          else R = mid - 1;
        }
      return L;
    }
    
    int main()
    {
      n = read();
      build(root[0], 1, n);
      for(int i = 1; i <= n; ++i) a[i] = b[i] = read();
      sort(b + 1, b + n + 1);
      _n = unique(b + 1, b + n + 1) - b - 1;
      for(int i = 1; i <= n; ++i)
        {
          a[i] = lower_bound(b + 1, b + _n + 1, a[i]) - b;
          v[a[i]].push_back(i);
        }
      for(int i = 1; i <= _n; ++i)
        {
          root[i] = root[i - 1];
          for(int j = 0; j < (int)v[i - 1].size(); ++j)
    	insert(root[i], root[i], 1, n, v[i - 1][j]);
        }
      m = read();
      for(int i = 1, ans = 0; i <= m; ++i)
        {
          for(int j = 0; j < 4; ++j) q[j] = read();
          for(int j = 0; j < 4; ++j) q[j] = (q[j] + ans) % n + 1;
          sort(q, q + 4);
          ans = b[solve()];
          write(ans), enter;
        }
      return 0;
    }
    
  • 相关阅读:
    经典哦,男女3.8,8.3
    在web窗体设计器中未能加载该文件
    使用客户端脚本
    C#的数据类型
    实验下cookie
    C#中Split分隔字符串的应用
    未将对象引用设置到对象的实例
    System.StackOverflowException 的异常;jit调试失败
    DataGrid中添加删除确认对话框 多种实现
    常用正则表达式
  • 原文地址:https://www.cnblogs.com/mrclr/p/10074763.html
Copyright © 2011-2022 走看看