[BZOJ3600]没有人的算术
[BZOJ3600]没有人的算术
试题描述
输入
见“试题描述”
输出
见“试题描述”
输入示例
见“试题描述”
输出示例
见“试题描述”
数据规模及约定
见“试题描述”
题解
今天读了读 WJMZBMR 的论文,了解了重量平衡树的一些基本常识,也借此机会学了一发替罪羊树。
替罪羊树与 treap 的区别在于它复杂度的保证不基于旋转操作,而是对子树的暴力重构。这样,一些子树信息就能够很容易地维护了,并且这样一来它也能做树套树外层的那层树。
这题我做法就是离线把每个可能出现的数用实数表示出来,详细的表示方法可以参见陈立杰论文《重量平衡树和后缀平衡树在信息学奥赛中的应用》,百度一下都能搜到,然后就是裸的线段树点修改区间询问问题了。
这题的突破口在于每种可能的数都是在当前状态的基础之上产生的新数,而当前状态我们已经清楚所有数的大小关系了,所以组合之后的数我们还是能够比较方便的知道它和每个数的大小关系,从而能够准确地在替罪羊树中找到插入的位置。
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define maxn 100010 #define maxm 500010 const double rtl = 0.0, rtr = 100000.0; struct Node { int lv, rv, siz; double L, R; double calc() const { return (L + R) * .5; } Node() {} Node(int _1, int _2, double _3, double _4): lv(_1), rv(_2), L(_3), R(_4) {} bool operator == (const Node& t) const { return lv == t.lv && rv == t.rv; } } ns[maxm]; bool operator < (const Node& a, const Node& b) { if(a.lv && !b.lv) return 0; if(!a.lv && b.lv) return 1; if(a.rv && !b.rv) return 0; if(!a.rv && b.rv) return 1; double av = ns[a.lv].calc(), bv = ns[b.lv].calc(); if(av != bv) return av < bv; av = ns[a.rv].calc(); bv = ns[b.rv].calc(); return av < bv; } int rt, ToT, fa[maxm], ch[maxm][2]; void maintain(int o) { ns[o].siz = 1; for(int i = 0; i < 2; i++) if(ch[o][i]) ns[o].siz += ns[ch[o][i]].siz; return ; } const double Bili = .6; bool unbal(int o) { return max(ch[o][0] ? ns[ch[o][0]].siz : 0, ch[o][1] ? ns[ch[o][1]].siz : 0) > Bili * ns[o].siz; } int No[maxm], cntn; void getnode(int o) { if(!o) return ; getnode(ch[o][0]); No[++cntn] = o; getnode(ch[o][1]); return ; } void build(int& o, int l, int r, double L, double R) { if(l > r){ o = 0; return ; } int mid = l + r >> 1; ns[o = No[mid]].L = L; ns[o].R = R; double M = ns[o].calc(); build(ch[o][0], l, mid - 1, L, M); build(ch[o][1], mid + 1, r, M, R); if(ch[o][0]) fa[ch[o][0]] = o; if(ch[o][1]) fa[ch[o][1]] = o; return maintain(o); } void rebuild(int& u, double L, double R) { cntn = 0; getnode(u); /*for(int i = 1; i <= cntn; i++) printf("%d%c", No[i], i < cntn ? ' ' : '\n'); printf("%d rebuild!\n", u); // */ build(u, 1, cntn, L, R); return ; } int rbp; int insert(int& o, Node x) { // printf("o: %d %d %d | %d %d\n", o, ns[o].lv, ns[o].rv, ch[o][0], ch[o][1]); if(!o) { ns[o = ++ToT] = x; return maintain(o), o; } if(ns[o] == x) return o; bool d = ns[o] < x; if(d) x.L = ns[o].calc(); else x.R = ns[o].calc(); int tmp = insert(ch[o][d], x); fa[ch[o][d]] = o; maintain(o); if(unbal(o)) rbp = o; return tmp; } int Insert(int lv, int rv) { rbp = 0; int u = insert(rt, Node(lv, rv, rtl, rtr)); if(rbp) { int frbp = fa[rbp]; double L = ns[rbp].L, R = ns[rbp].R; bool fl = 0, dir; if(!frbp) fl = 1; else dir = ch[frbp][1] == rbp; rebuild(rbp, L, R); fa[rbp] = frbp; if(fl) rt = rbp; else ch[frbp][dir] = rbp; } return u; } double Find(int o, Node x) { if(ns[o] == x) return ns[o].calc(); bool d = ns[o] < x; return Find(ch[o][d], x); } struct Que { int l, r, k; Que() {} Que(int _1, int _2, int _3): l(_1), r(_2), k(_3) {} } qs[maxm]; double val[maxn]; int mx[maxn<<2]; void build(int L, int R, int o) { if(L == R) mx[o] = L; else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; build(L, M, lc); build(M+1, R, rc); if(val[mx[lc]] > val[mx[rc]]) mx[o] = mx[lc]; if(val[mx[lc]] < val[mx[rc]]) mx[o] = mx[rc]; if(val[mx[lc]] == val[mx[rc]]) mx[o] = mx[lc]; } return ; } void update(int L, int R, int o, int p) { if(L == R) mx[o] = R; else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; if(p <= M) update(L, M, lc, p); else update(M+1, R, rc, p); if(val[mx[lc]] > val[mx[rc]]) mx[o] = mx[lc]; if(val[mx[lc]] < val[mx[rc]]) mx[o] = mx[rc]; if(val[mx[lc]] == val[mx[rc]]) mx[o] = mx[lc]; } return ; } int query(int L, int R, int o, int ql, int qr) { if(ql <= L && R <= qr) return mx[o]; int M = L + R >> 1, lc = o << 1, rc = lc | 1; int pos = 0; if(ql <= M) { int tmp = query(L, M, lc, ql, qr); if(!pos || val[pos] < val[tmp]) pos = tmp; } if(qr > M) { int tmp = query(M+1, R, rc, ql, qr); if(!pos || val[pos] < val[tmp]) pos = tmp; } return pos; } int A[maxn]; int main() { ns[rt = ToT = 1] = Node(0, 0, rtl, rtr); maintain(rt); int n = read(), q = read(); for(int i = 1; i <= n; i++) A[i] = 1; char cmd[2]; for(int i = 1; i <= q; i++) { scanf("%s", cmd); int l = read(), r = read(); if(cmd[0] == 'C') { // if(i >= 30000) printf("here %d\n", i); int k = read(); int ltot = ToT; A[k] = Insert(A[l], A[r]); // if(ToT > ltot) printf("add: %d %d %d %d %d\n", i, l, r, A[l], A[r]); qs[i] = Que(233, k, A[k]); } if(cmd[0] == 'Q') qs[i] = Que(l, r, 0); } val[1] = Find(rt, Node(0, 0, 233.0, 666.0)); for(int i = 2; i <= n; i++) val[i] = val[1]; build(1, n, 1); for(int i = 1; i <= q; i++) if(qs[i].k) { val[qs[i].r] = ns[qs[i].k].calc(); // for(int j = 1; j <= n; j++) printf("%.5lf%c", val[j], j < n ? ' ' : '\n'); update(1, n, 1, qs[i].r); } else printf("%d\n", query(1, n, 1, qs[i].l, qs[i].r)); // printf("%d\n", ToT); return 0; }