[BZOJ3600][没有人的算术][替罪羊树+线段树]

50 篇文章 0 订阅
8 篇文章 0 订阅

[BZOJ3600][没有人的算术][替罪羊树+线段树]

题目大意:

定义一种数,要么是0,要么是一个二元组,这个二元组两元都是数。

定义小于是:

  1. 0<(l,r)
  2. 如果 x<a ,那么 (x,y)>(a,b)
  3. 如果 x=a,y>b ,那么 (x,y)>(a,b)

定义等于是:

  1. 0=0
  2. 如果 x=a,y=b ,那么 (x,y)=(a,b)

现在有一个初始全0的数列,有两种操作

1.求 [l,r] 内最大数的坐标
2.将 a[k] 修改为 (a[l],a[r]

思路:

VFK大大的题,题解在这里:http://pan.baidu.com/s/1B0JNo
感觉这种用二元组构造数的方法好神奇QwQ,但是完全没有思路……

这道题的主要思路在于如何快速地比较两个数,如果我们可以快速地比较两个数,那么剩下的事情就是直接线段树区间求最大值。

如果我们把这些树都放到一棵平衡树上,那么要比较两个数可以直接比较两个数在树上的Rank。

但是在树上的插入的过程中仍需要 log(n) 次比较,怎么说?

我们可以在树上的每一个节点都开一个实数域的开区间 (l,r) ,其中根节点对应的是 (0,1) (l,r) 左儿子对应的是 (l,mid) ,右节点对应的是 (mid,r) 。( mid=(l+r)/2 )。

树高 log(n) ,因此最底层的精度要求是 2log(n)=1/n n<=1e5 double并不会出现问题。

定义映射 f(x)=mid ,可以证明如果 f(x)<f(y) ,那么 x<y

有了这个映射,我们就可以在 O(1) 的时间内比较两个数的大小。

每次插入新数的时候,构成它的二元组的左边和右边都已经在之前求出了映射,比较 (x,y) 就相当于比较 (f(x),f(y)) 。应此我们依然可以在 O(1) 的时间内比较新数和旧数。

还有一个问题就是:由于需要维护树的平衡,一般的平衡树需要进行旋转操作,而旋转操作会把我们之前维护好的映射给打破。

这时候我们就可以使用重量平衡树,替罪羊树就是一种很好的选择,它并不通过旋转,而是在某棵子树不平衡到一定程度后暴力重构整棵树。映射 f(x) 依然得以维护。

有关替罪羊树可以看我的这篇专栏:https://zhuanlan.zhihu.com/p/21263304?refer=g1n0st

注意,平衡树中不应存在两个大小相同的数,而且要注意0是特殊的。

代码:
#include <bits/stdc++.h>
using namespace std;
const int Maxn = 1000010;
inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; static char c; bool minus = false;
    for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
    for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
}
inline void read(char &x) {
  x = get();
  for (; !(x >= 'A' && x <= 'Z'); ) x = get();
}
inline void write(int x) {
    if (!x) return (void) puts("0");
    if (x < 0) putchar('-'), x = -x;
    static short s[12], t;
    while (x) s[++t] = x % 10, x /= 10;
    while (t) putchar('0' + s[t--]);
    putchar('\n');
}
double a[Maxn];
struct data {
    int l, r;
    data(void) {}
    data(const int &l, const int &r) : l(l), r(r) {}
    friend bool operator > (const data &x, const data &y) {
        if (a[x.l] == a[y.l]) return a[x.r] > a[y.r];
        else return a[x.l] > a[y.l];
    }
    friend bool operator == (const data &x, const data &y) {
        return x.l == y.l && x.r == y.r;
    }
};
int id[Maxn], top, R, rt;
inline int Max(const int &a, const int &b) {
    return a > b ? a : b;
}
inline int Min(const int &a, const int &b) {
    return a < b ? a : b;
}
struct Sctree {
    int cnt;
    data v[Maxn];
    int size[Maxn], ls[Maxn], rs[Maxn];
    void dfs(int k) {
        if (!k) return;
        dfs(ls[k]); id[++top] = k;
        dfs(rs[k]);
    }
    inline void build(int &k, int l, int r, double lv, double rv) {
        if (l > r) {
            k = 0; return;
        }
        double mv = (lv + rv) / 2.0;
        int mid = (l + r) >> 1;
        k = id[mid]; a[k] = mv;
        build(ls[k], l, mid - 1, lv, mv);
        build(rs[k], mid + 1, r, mv, rv);
        size[k] = size[ls[k]] + size[rs[k]] + 1;
    }
    inline void rebuild(int &k, double lv, double rv) {
        top = 0;
        dfs(k);
        build(k, 1, top, lv, rv);
    }
    inline int insert(int &k, double lv, double rv, data val) {
        double mv = (lv + rv) / 2.0;
        if (!k) {
            k = ++cnt; 
            a[k] = mv, v[k] = val, size[k] = 1;
            return k;
        }
        int p;
        if (val == v[k]) return k;
        else {
            size[k]++;
            if (val > v[k]) p = insert(rs[k], mv, rv, val);
            else p = insert(ls[k], lv, mv, val);
        }
        if (size[k] * 0.75 > Max(size[ls[k]], size[rs[k]])) {
            if (R) {
                if (R == ls[k]) rebuild(ls[k], lv, mv);
                else rebuild(rs[k], mv, rv);
                R = 0;
            }
        } else R = k;
        return p;
    }
} t;
int mxT[Maxn << 2], pos[Maxn];
inline void modify(int o, int l, int r, int v) {
  if (l == r) {
    return (void)(mxT[o] = l);
  }
  int mid = (l + r) >> 1;
  if (mid >= v) modify(o << 1, l, mid, v);
  else modify(o << 1 | 1, mid + 1, r, v);
  int x = mxT[o << 1], y = mxT[o << 1 | 1];
  if (a[pos[x]] >= a[pos[y]]) mxT[o] = x;
  else mxT[o] = y;
}
inline int query(int o, int l, int r, int L, int R) {
  if (l >= L && r <= R) {
    return mxT[o];
  }
  int mid = (l + r) >> 1, rt, ret = 0;
  if (mid >= L) {
    rt = query(o << 1, l, mid, L, Min(mid, R));
    if (a[pos[rt]] > a[pos[ret]]) ret = rt;
  }
  if (mid < R) {
    rt = query(o << 1 | 1, mid + 1, r, Max(L, mid + 1), R);
    if (a[pos[rt]] > a[pos[ret]]) ret = rt;
  }
  return ret;
}
int n, m;
int main(void) {
  //freopen("in.txt", "r", stdin);
  //freopen("out1.txt", "w", stdout);
  a[0] = -1;
  t.insert(rt, 0, 1, data(0, 0));
  read(n), read(m);
  for (register int i = 1; i <= n; i++) pos[i] = 1;
  for (register int i = 1; i <= n; i++) 
    modify(1, 1, n, i);
  char op; int x, y, k;
  while (m--) {
    read(op), read(x), read(y);
    if (op == 'Q') write(query(1, 1, n, x, y));
    else {
      read(k);
      pos[k] = t.insert(rt, 0, 1, data(pos[x], pos[y]));
      if (R) t.rebuild(rt, 0, 1), R = 0;
      modify(1, 1, n, k);
    }
  }
  return 0;
}

替罪羊树的代码貌似并不能直接用我专栏中的代码,不然两个结构体会相互嵌套……

完。

By g1n0st

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值