• [bzoj3524==bzoj2223][Poi2014]Couriers/[Coci 2009]PATULJCI——主席树+权值线段树


    题目大意

    给定一个大小为n,每个数的大小均在[1,c]之间的数列,你需要回答m个询问,其中第i个询问形如((l_i, r_i)),你需要回答是否存在一个数使得它在区间([l_i,r_i])中出现至少(frac{r-l+1}{2})次。

    题解

    第一次写主席树。
    不难发现,对于一个询问,只有可能要么有解,要么有一个解。
    考虑到每个数均在一个确定的区间内,我们考虑开一棵权值线段树(以前一直用这种方法,但不知到这就是权值线段树)来记录每一个数字的出现次数。
    考虑到他要求询问一个区间,我们只要知道在后一个时间点树的情况和前一个时间点的树的情况就好了。但是,我们需要修改线段树,所以我们需要使用一个可持久化数据结构来实现这种修改与查询。
    具体地,我们考虑对于每次修改建一棵新树来存储这个时间点的情况。显然这个时间点可以从上一个时间点推出来。
    如果我们真的建一棵新树,显然会爆空间。我们考虑仅仅在修改过的节点新建节点,同时把边乱连合理地连接即可。
    对于每一个询问,我们二分答案即可。
    至于复杂度,时间复杂度的话就是(Theta(跑得过)),但空间复杂度就是(Theta(不一定能跑过))辣(逃
    不过在bzoj改数据以后还是能过的。

    注意

    二分答案的时候,注意返回0的情况。也就是说,充分考虑到每一种情况并为其设立出口。

    代码(bzoj3524)

    #include <cstdio>
    int n, m, sz;
    const int maxn = 500010;
    const int maxm = 10000010;
    int rt[maxn], lc[maxm], rc[maxm], sum[maxm];
    void update(int l, int r, int x, int &y, int v) {
      y = ++sz;
      sum[y] = sum[x] + 1;
      if (l == r)
        return;
      lc[y] = lc[x];
      rc[y] = rc[x];
      int mid = (l + r) >> 1;
      if (v <= mid)
        update(l, mid, lc[x], lc[y], v);
      else
        update(mid + 1, r, rc[x], rc[y], v);
    }
    int que(int L, int R) {
      int l = 1, r = n, mid, x, y, tmp = (R - L + 1) >> 1;
      x = rt[L - 1];
      y = rt[R];
      while (l != r) {
        if (sum[y] - sum[x] <= tmp)
          return 0;
        mid = (l + r) >> 1;
        if (sum[lc[y]] - sum[lc[x]] > tmp) {
          r = mid;
          x = lc[x];
          y = lc[y];
        } else if (sum[rc[y]] - sum[rc[x]] > tmp) {
          l = mid + 1;
          x = rc[x];
          y = rc[y];
        } else
          return 0;
      }
      return l;
    }
    int main() {
    #ifdef D
      freopen("input", "r", stdin);
    #endif
      scanf("%d %d", &n, &m);
      for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        update(1, n, rt[i - 1], rt[i], x);
      }
      for (int i = 1; i <= m; i++) {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d
    ", que(l, r));
      }
    }
    

    代码(bzoj2223)

    #include <cstdio>
    int n, m, a, sz;
    const int maxn = 500010;
    const int maxm = 10000010;
    int rt[maxn], lc[maxm], rc[maxm], sum[maxm];
    void update(int l, int r, int x, int &y, int v) {
      y = ++sz;
      sum[y] = sum[x] + 1;
      if (l == r)
        return;
      lc[y] = lc[x];
      rc[y] = rc[x];
      int mid = (l + r) >> 1;
      if (v <= mid)
        update(l, mid, lc[x], lc[y], v);
      else
        update(mid + 1, r, rc[x], rc[y], v);
    }
    int que(int L, int R) {
      int l = 1, r = a, mid, x, y, tmp = (R - L + 1) >> 1;
      x = rt[L - 1];
      y = rt[R];
      while (l != r) {
        if (sum[y] - sum[x] <= tmp)
          return 0;
        mid = (l + r) >> 1;
        if (sum[lc[y]] - sum[lc[x]] > tmp) {
          r = mid;
          x = lc[x];
          y = lc[y];
        } else if (sum[rc[y]] - sum[rc[x]] > tmp) {
          l = mid + 1;
          x = rc[x];
          y = rc[y];
        } else
          return 0;
      }
      return l;
    }
    int main() {
    #ifdef D
      freopen("input", "r", stdin);
    #endif
      scanf("%d %d", &n, &a);
      for (int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);
        update(1, a, rt[i - 1], rt[i], x);
      }
      scanf("%d", &m);
      for (int i = 1; i <= m; i++) {
        int l, r;
        scanf("%d %d", &l, &r);
        int ans = que(l, r);
        if (ans > 0) {
          printf("yes %d
    ", ans);
        } else
          printf("no
    ");
      }
    }
    
  • 相关阅读:
    asp结合swf外调代码
    loadMovie与loadMovieNum区别
    行业常用名词及缩写定义
    用javascript 控制跳转页面
    jdk源码整数和字符串间的转换
    动态代理动态生成java文件并编译成class文件
    JDK源码分析——动态代理源码(Proxy类)
    策略模式与工厂模式比较
    Java编程:常见问题汇总
    工厂方法与抽象工厂的区别
  • 原文地址:https://www.cnblogs.com/gengchen/p/6496521.html
Copyright © 2020-2023  润新知