[BZOJ3600][没有人的算术][替罪羊树+线段树]
题目大意:
定义一种数,要么是0,要么是一个二元组,这个二元组两元都是数。
定义小于是:
- 0<(l,r)
- 如果 x<a ,那么 (x,y)>(a,b)
- 如果 x=a,y>b ,那么 (x,y)>(a,b)
定义等于是:
- 0=0
- 如果 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) ,因此最底层的精度要求是 2−log(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