mirror of
https://github.com/Kiritow/OJ-Problems-Source.git
synced 2024-03-22 13:11:29 +08:00
2210 lines
61 KiB
C++
2210 lines
61 KiB
C++
|
//一维树状数组
|
|||
|
//单点修改 + 单点查询 + 区间修改 + 区间查询 + 区间最值
|
|||
|
int n;
|
|||
|
template<typename T> struct BIT {
|
|||
|
T A[N]; //T B[N]; //区间增减/维护最值
|
|||
|
int lowbit(int x) { return x & -x; }
|
|||
|
void init() { memset(A, 0, sizeof(A)); /*memset(B, 0, sizeof(B));*/ }
|
|||
|
void update(int i, T v) { while (i <= n) { A[i] += v; i += lowbit(i); } }
|
|||
|
T query(int i) { T ret = 0; while (i) { ret += A[i]; i -= lowbit(i); } return ret; }
|
|||
|
T query(int i, int j) { return query(j) - query(i - 1); }
|
|||
|
//区间修改
|
|||
|
T query(int x) {
|
|||
|
if (!x) { return 0; }
|
|||
|
T ret1 = 0, ret2 = 0;
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { ret1 += A[i]; }
|
|||
|
for (int i = x - 1; i > 0; i -= lowbit(i)) { ret2 += B[i]; }
|
|||
|
return ret1 * x + ret2;
|
|||
|
}
|
|||
|
void update(int x, T v) {
|
|||
|
for (int i = x; i > 0; i -= lowbit(i)) { A[i] += v; }
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { B[i] += x * v; }
|
|||
|
}
|
|||
|
void update(int i, int j, T v) { update(j, v); if (i > 1) { update(i - 1, -v); } }
|
|||
|
//维护区间最值 O(log^2(n))
|
|||
|
void modify(int x, T v) {
|
|||
|
B[x] = v;
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) {
|
|||
|
A[i] = max(A[i], v);
|
|||
|
for (int j = 1; j < lowbit(i); j <<= 1) {
|
|||
|
A[i] = max(A[i], A[i - j]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
T query(int l, int r) {
|
|||
|
T ret = B[r];
|
|||
|
while (true) {
|
|||
|
ret = max(ret, B[r]);
|
|||
|
if (l == r) { break; }
|
|||
|
for (r -= 1; r - l >= lowbit(r); r -= lowbit(r)) { ret = max(ret, A[r]); }
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
//求区间第K大的下标/值 O(log^2(n))
|
|||
|
int getK(int l, int r, int k) {
|
|||
|
while (l <= r) {
|
|||
|
int mid = l + ((r - l) >> 1);
|
|||
|
if (query(mid) >= k) { r = mid - 1; }
|
|||
|
else { l = mid + 1; }
|
|||
|
}
|
|||
|
return l; //A[l]
|
|||
|
}
|
|||
|
};
|
|||
|
BIT<int> bit;
|
|||
|
//二维树状数组
|
|||
|
//单点修改 + 单点查询 + 区域修改 + 区域查询
|
|||
|
int n, m;
|
|||
|
template<typename T> struct BIT {
|
|||
|
T A[N][N]; //T B[N][N], C[N][N], D[N][N]; //区域求和
|
|||
|
int lowbit(int x) { return x & -x; }
|
|||
|
void init() { memset(A, 0, sizeof(A)); /*memset(B, 0, sizeof(B)); memset(C, 0, sizeof(C)); memset(D, 0, sizeof(D));*/ }
|
|||
|
T get(int x, int y) {
|
|||
|
T ret = 0;
|
|||
|
for (int i = x; i > 0; i -= lowbit(i)) { for (int j = y; j > 0; j -= lowbit(j)) { ret += A[i][j]; } }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
T query(int x, int y) { return get(x, y) - get(x, y - 1) - get(x - 1, y) + get(x - 1, y - 1); }
|
|||
|
void update(int x, int y, T v) {
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { for (int j = y; j <= m; j += lowbit(j)) { A[i][j] += v; } }
|
|||
|
}
|
|||
|
//区域和[x1][y1]-[x2][y2]
|
|||
|
T query(int x1, int y1, int x2, int y2) {
|
|||
|
return get(x2, y2) - get(x1 - 1, y2) - get(x2, y1 - 1) + get(x1 - 1, y1 - 1);
|
|||
|
}
|
|||
|
//区域增减
|
|||
|
void update(int x, int y, T v, T a[][N]) {
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { for (int j = y; j <= m; j += lowbit(j)) { a[i][j] += v; } }
|
|||
|
}
|
|||
|
void update(int x1, int y1, int x2, int y2, T v) {
|
|||
|
update(x1, y1, v, A); update(x2 + 1, y1, -v, A);
|
|||
|
update(x1, y2 + 1, -v, A); update(x2 + 1, y2 + 1, v, A);
|
|||
|
update(x1, y1, v * x1, B); update(x2 + 1, y1, -v * (x2 + 1), B);
|
|||
|
update(x1, y2 + 1, -v * x1, B); update(x2 + 1, y2 + 1, v * (x2 + 1), B);
|
|||
|
update(x1, y1, v * y1, C); update(x2 + 1, y1, -v * y1, C);
|
|||
|
update(x1, y2 + 1, -v * (y2 + 1), C); update(x2 + 1, y2 + 1, v * (y2 + 1), C);
|
|||
|
update(x1, y1, v * x1 * y1, D); update(x2 + 1, y1, -v * (x2 + 1) * y1, D);
|
|||
|
update(x1, y2 + 1, -v * x1 * (y2 + 1), D); update(x2 + 1, y2 + 1, v * (x2 + 1) * (y2 + 1), D);
|
|||
|
}
|
|||
|
};
|
|||
|
BIT<int> bit;
|
|||
|
//线段树 单点修改 + 区间查询
|
|||
|
#define lson l,m,rt<<1
|
|||
|
#define rson m+1,r,rt<<1|1
|
|||
|
template<typename T> struct SegmentTree {
|
|||
|
T data[N << 2];
|
|||
|
T calc(const T &x, const T &y)const { return x + y; }
|
|||
|
void push_up(int rt) { data[rt] = calc(data[rt << 1], data[rt << 1 | 1]); }
|
|||
|
void build(int l, int r, int rt) {
|
|||
|
if (l == r) { scanf("%d", &data[rt]); return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
build(lson);
|
|||
|
build(rson);
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
void update(int p, T val, int l, int r, int rt) {
|
|||
|
if (l == r) { data[rt] += val; return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
if (p <= m) { update(p, val, lson); }
|
|||
|
else { update(p, val, rson); }
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
T query(int L, int R, int l, int r, int rt) {
|
|||
|
if (L <= l && r <= R) { return data[rt]; }
|
|||
|
int m = (l + r) >> 1; T ret = 0;
|
|||
|
if (L <= m) { ret = calc(ret, query(L, R, lson)); }
|
|||
|
if (m < R) { ret = calc(ret, query(L, R, rson)); }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
};
|
|||
|
SegmentTree<int> st;
|
|||
|
//线段树 区间查询/修改 + 延迟标记
|
|||
|
#define lson l,m,rt<<1
|
|||
|
#define rson m+1,r,rt<<1|1
|
|||
|
template<typename T> struct SegmentTree {
|
|||
|
T data[N << 2], lazy[N << 2];
|
|||
|
T calc(const T &x, const T &y)const { return x + y; }
|
|||
|
void push_up(int rt) { data[rt] = calc(data[rt << 1], data[rt << 1 | 1]); }
|
|||
|
void push_down(int rt, int len) {
|
|||
|
if (lazy[rt]) {
|
|||
|
data[rt << 1] += lazy[rt] * (len - (len >> 1)); lazy[rt << 1] += lazy[rt];
|
|||
|
data[rt << 1 | 1] += lazy[rt] * (len >> 1); lazy[rt << 1 | 1] += lazy[rt];
|
|||
|
lazy[rt] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void build(int l, int r, int rt) {
|
|||
|
lazy[rt] = 0;
|
|||
|
if (l == r) { scanf("%d", &data[rt]); return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
build(lson);
|
|||
|
build(rson);
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
void update(int L, int R, T val, int l, int r, int rt) {
|
|||
|
if (L <= l && r <= R) {
|
|||
|
data[rt] += val * (r - l + 1);
|
|||
|
lazy[rt] += val;
|
|||
|
return;
|
|||
|
}
|
|||
|
push_down(rt, r - l + 1);
|
|||
|
int m = (l + r) >> 1;
|
|||
|
if (L <= m) { update(L, R, val, lson); }
|
|||
|
if (m < R) { update(L, R, val, rson); }
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
T query(int L, int R, int l, int r, int rt) {
|
|||
|
if (L <= l && r <= R) { return data[rt]; }
|
|||
|
push_down(rt, r - l + 1);
|
|||
|
int m = (l + r) >> 1; T ret = 0;
|
|||
|
if (L <= m) { ret = calc(ret, query(L, R, lson)); }
|
|||
|
if (m < R) { ret = calc(ret, query(L, R, rson)); }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
};
|
|||
|
SegmentTree<int> st;
|
|||
|
//非递归版线段树 单点修改 + 区间查询
|
|||
|
const int N = ((131072 << 1) + 10); //节点个数->不小于区间长度+2的最小2的正整数次幂*2+10
|
|||
|
#define l(x) ((x)<<1) //x的左儿子
|
|||
|
#define r(x) (((x)<<1)|1) //x的右儿子
|
|||
|
template<typename T> struct zkwSegmentTree {
|
|||
|
int m; //底层节点数
|
|||
|
T sum[N]; //区间和
|
|||
|
void build(int n) {
|
|||
|
for (m = 1; m < n + 2; m <<= 1);
|
|||
|
for (int i = 1; i <= n; i++) { scanf("%d", &sum[m + i]); }
|
|||
|
for (int i = m - 1; i; i--) { sum[i] = sum[l(i)] + sum[r(i)]; }
|
|||
|
}
|
|||
|
void update(int p, T val) {
|
|||
|
for (sum[p += m] += val, p >>= 1; p; p >>= 1) {
|
|||
|
sum[p] = sum[l(p)] + sum[r(p)];
|
|||
|
}
|
|||
|
}
|
|||
|
T query(int l, int r) {
|
|||
|
T ret = 0;
|
|||
|
for (l += m - 1, r += m + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
|
|||
|
if (~l & 1) { ret += sum[l ^ 1]; }
|
|||
|
if (r & 1) { ret += sum[r ^ 1]; }
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
};
|
|||
|
zkwSegmentTree<int> st;
|
|||
|
//非递归版线段树 区间查询/修改 + 延迟标记
|
|||
|
const int N = ((131072 << 1) + 10); //节点个数->不小于区间长度+2的最小2的正整数次幂*2+10
|
|||
|
#define l(x) ((x)<<1) //x的左儿子
|
|||
|
#define r(x) (((x)<<1)|1) //x的右儿子
|
|||
|
template<typename T> struct zkwSegmentTree {
|
|||
|
int m, h; //底层节点数 高度
|
|||
|
T sum[N], add[N]; //区间和 延迟标记
|
|||
|
void pushdown(int rt) {
|
|||
|
for (int i = h, p; i; i--) { //自顶向下
|
|||
|
if (add[p = rt >> i]) {
|
|||
|
add[p] >>= 1; //add[p]为节点增加总量, 子节点增加一半
|
|||
|
sum[l(p)] += add[p]; add[l(p)] += add[p];
|
|||
|
sum[r(p)] += add[p]; add[r(p)] += add[p];
|
|||
|
add[p] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
void build(int n) {
|
|||
|
for (m = 1, h = 0; m < n + 2; m <<= 1, h++);
|
|||
|
for (int i = 1; i <= n; i++) { scanf("%d", &sum[m + i]); }
|
|||
|
for (int i = m - 1; i; i--) { sum[i] = sum[l(i)] + sum[r(i)]; }
|
|||
|
}
|
|||
|
void update(int l, int r, T val) {
|
|||
|
l += m - 1, r += m + 1; int ll = l >> 1, rr = r >> 1;
|
|||
|
for (pushdown(l), pushdown(r); l ^ r ^ 1; l >>= 1, r >>= 1, val <<= 1) {
|
|||
|
if (~l & 1) { sum[l ^ 1] += val; add[l ^ 1] += val; }
|
|||
|
if (r & 1) { sum[r ^ 1] += val; add[r ^ 1] += val; }
|
|||
|
}
|
|||
|
for (; ll; ll >>= 1) { sum[ll] = sum[l(ll)] + sum[r(ll)]; }
|
|||
|
for (; rr; rr >>= 1) { sum[rr] = sum[l(rr)] + sum[r(rr)]; }
|
|||
|
}
|
|||
|
T query(int l, int r) {
|
|||
|
T ret = 0; l += m - 1, r += m + 1;
|
|||
|
for (pushdown(l), pushdown(r); l ^ r ^ 1; l >>= 1, r >>= 1) {
|
|||
|
if (~l & 1) { ret += sum[l ^ 1]; }
|
|||
|
if (r & 1) { ret += sum[r ^ 1]; }
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
};
|
|||
|
zkwSegmentTree<int> st;
|
|||
|
//可持久化线段树
|
|||
|
const int N = 100005;
|
|||
|
const int M = 2500005;
|
|||
|
const int INF = 0x3f3f3f3f;
|
|||
|
int n, m, nn; //离散化后大小
|
|||
|
#define lson l,m,ls[rt]
|
|||
|
#define rson m+1,r,rs[rt]
|
|||
|
template<typename T> struct SegmentTree {
|
|||
|
int ls[M], rs[M], root[N], tot; T data[M];
|
|||
|
int new_node() { return ++tot; }
|
|||
|
void build(int l, int r, int &rt) {
|
|||
|
rt = new_node(); data[rt] = 0;
|
|||
|
if (l == r) { return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
build(lson);
|
|||
|
build(rson);
|
|||
|
}
|
|||
|
void update(int p, T val, int lst, int l, int r, int &rt) {
|
|||
|
rt = new_node(); ls[rt] = ls[lst]; rs[rt] = rs[lst]; data[rt] = data[lst] + val;
|
|||
|
if (l == r) { return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
if (p <= m) { update(p, val, ls[lst], lson); }
|
|||
|
else { update(p, val, rs[lst], rson); }
|
|||
|
}
|
|||
|
//带修改区间第k小
|
|||
|
int tree[N], use[N];
|
|||
|
int lowbit(int x) { return x & -x; }
|
|||
|
void modify(int x, int p, T val) { //x为原数列中的下标, p为值
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { update(p, val, tree[i], 1, nn, tree[i]); }
|
|||
|
}
|
|||
|
T query(int x) {
|
|||
|
T ret = 0;
|
|||
|
for (int i = x; i; i -= lowbit(i)) { ret += data[ls[use[i]]]; }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
int query(int L, int R, int k, int l, int r) {
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = tree[i]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = tree[i]; }
|
|||
|
int lr = root[L], rr = root[R];
|
|||
|
while (l < r) {
|
|||
|
int m = (l + r) >> 1; T tmp = query(R) - query(L) + data[ls[rr]] - data[ls[lr]];
|
|||
|
if (k <= tmp) {
|
|||
|
r = m;
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = ls[use[i]]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = ls[use[i]]; }
|
|||
|
lr = ls[lr]; rr = ls[rr];
|
|||
|
} else {
|
|||
|
l = m + 1; k -= tmp;
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = rs[use[i]]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = rs[use[i]]; }
|
|||
|
lr = rs[lr]; rr = rs[rr];
|
|||
|
}
|
|||
|
}
|
|||
|
return l;
|
|||
|
}
|
|||
|
};
|
|||
|
SegmentTree<int> st;
|
|||
|
//ZOJ 2112
|
|||
|
int a[N], hs[N], l[N], r[N], k[N];
|
|||
|
char op[N];
|
|||
|
int main() {
|
|||
|
int C = 0, T;
|
|||
|
scanf("%d", &T);
|
|||
|
while (++C <= T) {
|
|||
|
nn = 0;
|
|||
|
scanf("%d%d", &n, &m);
|
|||
|
for (int i = 1; i <= n; i++) {
|
|||
|
scanf("%d", &a[i]); hs[++nn] = a[i];
|
|||
|
}
|
|||
|
for (int i = 0; i < m; i++) {
|
|||
|
scanf(" %c%d%d", &op[i], &l[i], &r[i]);
|
|||
|
switch (op[i]) {
|
|||
|
case 'Q': scanf("%d", &k[i]); break;
|
|||
|
case 'C': hs[++nn] = r[i]; break;
|
|||
|
}
|
|||
|
}
|
|||
|
sort(hs + 1, hs + nn + 1);
|
|||
|
nn = unique(hs + 1, hs + nn + 1) - hs - 1;
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
a[i] = lower_bound(hs + 1, hs + nn + 1, a[i]) - hs;
|
|||
|
}
|
|||
|
st.tot = 0;
|
|||
|
st.build(1, nn, st.root[0]);
|
|||
|
for (int i = 1; i <= n; i++) {
|
|||
|
st.update(a[i], 1, st.root[i - 1], 1, nn, st.root[i]);
|
|||
|
}
|
|||
|
for (int i = 1; i <= n; i++) { st.tree[i] = st.root[0]; }
|
|||
|
for (int i = 0; i < m; i++) {
|
|||
|
switch (op[i]) {
|
|||
|
case 'Q':
|
|||
|
printf("%d\n", hs[st.query(l[i] - 1, r[i], k[i], 1, nn)]);
|
|||
|
break;
|
|||
|
case 'C':
|
|||
|
st.modify(l[i], a[l[i]], -1);
|
|||
|
a[l[i]] = lower_bound(hs + 1, hs + nn + 1, r[i]) - hs;
|
|||
|
st.modify(l[i], a[l[i]], 1);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//实时开节点的权值线段树 (无需离散化) O(logV)
|
|||
|
const int N = 60005;
|
|||
|
const int M = 2500005;
|
|||
|
const int INF = 0x3f3f3f3f;
|
|||
|
int n, a[N];
|
|||
|
#define lson l,m,ls[rt]
|
|||
|
#define rson m+1,r,rs[rt]
|
|||
|
struct SegmentTree {
|
|||
|
int ls[M], rs[M], cnt[M], root[N], tot;
|
|||
|
void init() {
|
|||
|
tot = 0; memset(cnt, 0, sizeof(cnt)); memset(root, 0, sizeof(root));
|
|||
|
memset(ls, 0, sizeof(ls)); memset(rs, 0, sizeof(rs));
|
|||
|
}
|
|||
|
int new_node() { return ++tot; }
|
|||
|
void update(int p, int val, int l, int r, int &rt) {
|
|||
|
if (!rt) { rt = new_node(); }
|
|||
|
if (l == r) { cnt[rt] += val; return; }
|
|||
|
int m = (l + r) >> 1;
|
|||
|
if (p <= m) { update(p, val, lson); }
|
|||
|
else { update(p, val, rson); }
|
|||
|
cnt[rt] = cnt[ls[rt]] + cnt[rs[rt]];
|
|||
|
}
|
|||
|
int use[N];
|
|||
|
int lowbit(int x) { return x & -x; }
|
|||
|
//单点修改
|
|||
|
void modify(int x, int p, int val) {
|
|||
|
for (int i = x; i <= n; i += lowbit(i)) { update(p, val, 0, INF, root[i]); }
|
|||
|
}
|
|||
|
int query(int x) {
|
|||
|
int ret = 0;
|
|||
|
for (int i = x; i; i -= lowbit(i)) { ret += cnt[ls[use[i]]]; }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
//查询区间第k小
|
|||
|
int query(int L, int R, int k, int l, int r) {
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = root[i]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = root[i]; }
|
|||
|
while (l < r) {
|
|||
|
int m = (l + r) >> 1, tmp = query(R) - query(L);
|
|||
|
if (k <= tmp) {
|
|||
|
r = m;
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = ls[use[i]]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = ls[use[i]]; }
|
|||
|
} else {
|
|||
|
l = m + 1; k -= tmp;
|
|||
|
for (int i = L; i; i -= lowbit(i)) { use[i] = rs[use[i]]; }
|
|||
|
for (int i = R; i; i -= lowbit(i)) { use[i] = rs[use[i]]; }
|
|||
|
}
|
|||
|
}
|
|||
|
return l;
|
|||
|
}
|
|||
|
} st;
|
|||
|
//BZOJ1901 区间第k小
|
|||
|
int main() {
|
|||
|
int m, l, r, k; char op[5];
|
|||
|
while (~scanf("%d%d", &n, &m)) {
|
|||
|
st.init();
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &a[i]);
|
|||
|
st.modify(i, a[i], 1);
|
|||
|
}
|
|||
|
while (m--) {
|
|||
|
scanf("%s%d%d", op, &l, &r);
|
|||
|
switch (op[0]) {
|
|||
|
case 'Q':
|
|||
|
scanf("%d", &k);
|
|||
|
printf("%d\n", st.query(l - 1, r, k, 0, INF));
|
|||
|
break;
|
|||
|
case 'C':
|
|||
|
st.modify(l, a[l], -1);
|
|||
|
a[l] = r;
|
|||
|
st.modify(l, a[l], 1);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//平衡二叉树 常用操作
|
|||
|
//注意这些操作适用于不允许重复值的平衡二叉树(set而非multiset)
|
|||
|
//对于允许重复值(拥有cnt域)的实现, 只要在一些+1的地方稍作修改(改成cnt[x])即可
|
|||
|
bool find(int v) {
|
|||
|
for (int x = root; x; x = ch[x][key[x] < v]) {
|
|||
|
if (key[x] == v) { return true; }
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
int getKth(int k) {
|
|||
|
int x = root;
|
|||
|
while (size[ch[x][0]] + 1 != k) {
|
|||
|
if (k < size[ch[x][0]] + 1) { x = ch[x][0]; }
|
|||
|
else { k -= size[ch[x][0]] + 1; x = ch[x][1]; }
|
|||
|
}
|
|||
|
return key[x];
|
|||
|
}
|
|||
|
int getRank(int v) {
|
|||
|
int ret = 0, x = root;
|
|||
|
while (x) {
|
|||
|
if (v < key[x]) { x = ch[x][0]; }
|
|||
|
else { ret += size[ch[x][0]] + 1; x = ch[x][1]; }
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
int getPre(int v) {
|
|||
|
int x = root, y = 0;
|
|||
|
while (x) {
|
|||
|
if (v < key[x]) { x = ch[x][0]; }
|
|||
|
else { y = x; x = ch[x][1]; }
|
|||
|
}
|
|||
|
return y;
|
|||
|
}
|
|||
|
int getNext(int v) {
|
|||
|
int x = root, y = 0;
|
|||
|
while (x) {
|
|||
|
if (v > key[x]) { x = ch[x][1]; }
|
|||
|
else { y = x; x = ch[x][0]; }
|
|||
|
}
|
|||
|
return y;
|
|||
|
}
|
|||
|
int getMin() {
|
|||
|
if (size[root] == 0) { return -1; }
|
|||
|
int x = root;
|
|||
|
while (ch[x][0]) { x = ch[x][0]; }
|
|||
|
return x;
|
|||
|
}
|
|||
|
int getMax() {
|
|||
|
if (size[root] == 0) { return -1; }
|
|||
|
int x = root;
|
|||
|
while (ch[x][1]) { x = ch[x][1]; }
|
|||
|
return x;
|
|||
|
}
|
|||
|
//Debug遍历
|
|||
|
void treaval(int x) {
|
|||
|
if (x != 0) {
|
|||
|
treaval(ch[x][0]);
|
|||
|
printf("Node%2d:lson %2d rson %2d size = %2d ,val = %2d\n", x, ch[x][0], ch[x][1], size[x], key[x]);
|
|||
|
treaval(ch[x][1]);
|
|||
|
}
|
|||
|
}
|
|||
|
void debug() {
|
|||
|
printf("root:%d\n", root);
|
|||
|
treaval(root);
|
|||
|
putchar('\n');
|
|||
|
}
|
|||
|
//基于旋转的Treap 允许重复值
|
|||
|
//维护序列的有序性和堆的性质, 依靠堆值的随机化,
|
|||
|
//将树的高度维护在期望下平衡的程度,从而实现了各种操作期望O(logn)的复杂度.
|
|||
|
//它的性价比高在只有两种旋转(而且可以合并地写), 比红黑树和AVL短小
|
|||
|
struct Treap {
|
|||
|
int tot, root;
|
|||
|
int ch[N][2], key[N], pt[N], cnt[N], size[N];
|
|||
|
void init() { tot = root = 0; pt[0] = INF; }
|
|||
|
void push_up(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; }
|
|||
|
void new_node(int &x, int v) {
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
size[x] = cnt[x] = 1;
|
|||
|
pt[x] = rand();
|
|||
|
key[x] = v;
|
|||
|
}
|
|||
|
void rotate(int &x, int f) {
|
|||
|
int y = ch[x][f];
|
|||
|
ch[x][f] = ch[y][f ^ 1];
|
|||
|
ch[y][f ^ 1] = x;
|
|||
|
push_up(x);
|
|||
|
push_up(y);
|
|||
|
x = y;
|
|||
|
}
|
|||
|
void insert(int &x, int v) {
|
|||
|
if (!x) { new_node(x, v); return; }
|
|||
|
if (key[x] == v) {
|
|||
|
++cnt[x];
|
|||
|
} else {
|
|||
|
int f = key[x] < v;
|
|||
|
insert(ch[x][f], v);
|
|||
|
if (pt[ch[x][f]] < pt[x]) {
|
|||
|
rotate(x, f);
|
|||
|
}
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void erase(int &x, int v) {
|
|||
|
if (!x) { return; }
|
|||
|
if (key[x] == v) {
|
|||
|
if (cnt[x] > 1) {
|
|||
|
--cnt[x];
|
|||
|
} else {
|
|||
|
if (!ch[x][0] && !ch[x][1]) {
|
|||
|
x = 0;
|
|||
|
} else {
|
|||
|
rotate(x, pt[ch[x][0]] > pt[ch[x][1]]);
|
|||
|
erase(x, v);
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
erase(ch[x][key[x] < v], v);
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void insert(int v) { insert(root, v); }
|
|||
|
void erase(int v) { erase(root, v); }
|
|||
|
} treap;
|
|||
|
//Size Balanced Tree 不允许重复值
|
|||
|
//独特的平摊时间O(1)的Maintain操作, 具有仅次于红黑树的优秀的时间效率
|
|||
|
struct SBT {
|
|||
|
int root, tot;
|
|||
|
int ch[N][2], key[N], size[N];
|
|||
|
void init() { tot = root = 0; size[0] = 0; }
|
|||
|
void rotate(int &x, int f) {
|
|||
|
int y = ch[x][f];
|
|||
|
ch[x][f] = ch[y][f ^ 1];
|
|||
|
ch[y][f ^ 1] = x;
|
|||
|
size[y] = size[x];
|
|||
|
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
|
|||
|
x = y;
|
|||
|
}
|
|||
|
void maintain(int &x, int f) {
|
|||
|
if (size[ch[ch[x][f]][f]] > size[ch[x][f ^ 1]]) {
|
|||
|
rotate(x, f);
|
|||
|
} else if (size[ch[ch[x][f]][f ^ 1]] > size[ch[x][f ^ 1]]) {
|
|||
|
rotate(ch[x][f], f ^ 1); rotate(x, f);
|
|||
|
} else {
|
|||
|
return;
|
|||
|
}
|
|||
|
maintain(ch[x][0], 0);
|
|||
|
maintain(ch[x][1], 1);
|
|||
|
maintain(x, 0);
|
|||
|
maintain(x, 1);
|
|||
|
}
|
|||
|
void insert(int &x, int v) {
|
|||
|
if (!x) {
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
size[x] = 1;
|
|||
|
key[x] = v;
|
|||
|
} else {
|
|||
|
++size[x];
|
|||
|
insert(ch[x][key[x] < v], v);
|
|||
|
maintain(x, key[x] < v);
|
|||
|
}
|
|||
|
}
|
|||
|
int erase(int &x, int v) {
|
|||
|
if (!x) { return 0; }
|
|||
|
--size[x];
|
|||
|
if (key[x] == v || (key[x] > v && !ch[x][0]) || (key[x] < v && !ch[x][1])) {
|
|||
|
int ret = key[x];
|
|||
|
if (ch[x][0] && ch[x][1]) {
|
|||
|
key[x] = erase(ch[x][0], v + 1);
|
|||
|
} else {
|
|||
|
x = ch[x][0] + ch[x][1];
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
return erase(ch[x][key[x] < v], v);
|
|||
|
}
|
|||
|
void insert(int v) { insert(root, v); }
|
|||
|
void erase(int v) { erase(root, v); }
|
|||
|
} sbt;
|
|||
|
//Splay
|
|||
|
//可以实现很多其它平衡树无法实现的操作(如区间翻转), 既可以维护集合信息,
|
|||
|
//也可以维护序列信息, 可以用它来做Treap的题, 也可以用它来做线段树的题.
|
|||
|
//更重要的是, Splay可以实现split(将某棵子树从原树中分离)和merge操作(将某棵子树插入另一棵树),
|
|||
|
//这也使得区间插入删除成为可能. 它的美中不足是常数稍大, 约是Treap的1.5~3倍, 线段树的2~5倍.
|
|||
|
//Splay有单旋和双旋两种实现, 其中只有双旋保证了均摊O(logn)的单次操作复杂度,
|
|||
|
//但因为很多人认为zigzag太长不好敲(大多是OI选手有此困扰), 选择了单旋.
|
|||
|
//其实完全可以稍微损失一点常数, 合并成一个rotate函数来完成双旋.
|
|||
|
//此外一个良好的实现通常要在序列一首一尾增加两个哨兵节点, 这样可以减少很多边界特判.
|
|||
|
//有必要进行的扩展性说明是, 对于一棵树, 如果想要维护子树信息, 我们可以用Splay维护这棵树的括号序列(dfs序),
|
|||
|
//这样便可以轻易split出任意子树所属的区间;而用Splay维护dfs序的结构, 就是Euler-Tour Tree.
|
|||
|
//同样的, 如果想要维护链上信息, 可以先树链剖分然后用Splay维护每条重链,
|
|||
|
//根据杨哲在07年国家集训队作业的计算, 因其势能分析得到的复杂度依然是单次操作均摊O(logn)复杂度;
|
|||
|
//而类似的思想做些转化, 就变成了后面会提到的Link-Cut Tree(以下简称LCT).
|
|||
|
//ver.1
|
|||
|
#define keyTree (ch[ch[root][1]][0])
|
|||
|
const int N = 200005;
|
|||
|
const int INF = 0x3f3f3f3f;
|
|||
|
int num[N];
|
|||
|
struct Splay {
|
|||
|
int root, tot1, tot2;
|
|||
|
int ch[N][2], pre[N], size[N];
|
|||
|
int gc[N], que[N];
|
|||
|
int key[N], vmin[N], add[N], rev[N];
|
|||
|
void rotate(int x, int f) {
|
|||
|
int y = pre[x];
|
|||
|
ch[y][f ^ 1] = ch[x][f];
|
|||
|
pre[ch[x][f]] = y;
|
|||
|
pre[x] = pre[y];
|
|||
|
if (pre[x]) {
|
|||
|
ch[pre[y]][ch[pre[y]][1] == y] = x;
|
|||
|
}
|
|||
|
ch[x][f] = y;
|
|||
|
pre[y] = x;
|
|||
|
push_up(y);
|
|||
|
}
|
|||
|
void splay(int x, int goal) {
|
|||
|
push_down(x);
|
|||
|
while (pre[x] != goal) {
|
|||
|
int y = pre[x], z = pre[y];
|
|||
|
if (z == goal) {
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
rotate(x, ch[y][0] == x);
|
|||
|
} else {
|
|||
|
push_down(z);
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
int f = ch[z][0] == y;
|
|||
|
if (ch[y][f] == x) {
|
|||
|
rotate(x, f ^ 1);
|
|||
|
} else {
|
|||
|
rotate(y, f);
|
|||
|
}
|
|||
|
rotate(x, f);
|
|||
|
}
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
if (goal == 0) {
|
|||
|
root = x;
|
|||
|
}
|
|||
|
}
|
|||
|
void rotate_to(int k, int goal) {
|
|||
|
int x = root;
|
|||
|
push_down(x);
|
|||
|
while (size[ch[x][0]] != k) {
|
|||
|
if (k < size[ch[x][0]]) {
|
|||
|
x = ch[x][0];
|
|||
|
} else {
|
|||
|
k -= size[ch[x][0]] + 1;
|
|||
|
x = ch[x][1];
|
|||
|
}
|
|||
|
push_down(x);
|
|||
|
}
|
|||
|
splay(x, goal);
|
|||
|
}
|
|||
|
void erase(int x) {
|
|||
|
int fa = pre[x], head = 0, tail = 0;
|
|||
|
for (que[tail++] = x; head < tail; ++head) {
|
|||
|
gc[tot2++] = que[head];
|
|||
|
if (ch[que[head]][0]) {
|
|||
|
que[tail++] = ch[que[head]][0];
|
|||
|
}
|
|||
|
if (ch[que[head]][1]) {
|
|||
|
que[tail++] = ch[que[head]][1];
|
|||
|
}
|
|||
|
}
|
|||
|
ch[fa][ch[fa][1] == x] = 0;
|
|||
|
push_up(fa);
|
|||
|
}
|
|||
|
void new_node(int &x, int v, int fa) {
|
|||
|
if (tot2) {
|
|||
|
x = gc[--tot2];
|
|||
|
} else {
|
|||
|
x = ++tot1;
|
|||
|
}
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
pre[x] = fa;
|
|||
|
size[x] = 1;
|
|||
|
key[x] = vmin[x] = v;
|
|||
|
add[x] = rev[x] = 0;
|
|||
|
}
|
|||
|
void update_add(int x, int d) {
|
|||
|
if (x) {
|
|||
|
key[x] += d;
|
|||
|
add[x] += d;
|
|||
|
vmin[x] += d;
|
|||
|
}
|
|||
|
}
|
|||
|
void update_rev(int x) {
|
|||
|
if (x) {
|
|||
|
swap(ch[x][0], ch[x][1]);
|
|||
|
rev[x] ^= 1;
|
|||
|
}
|
|||
|
}
|
|||
|
void push_up(int x) {
|
|||
|
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
|
|||
|
vmin[x] = min(key[x], min(vmin[ch[x][0]], vmin[ch[x][1]]));
|
|||
|
}
|
|||
|
void push_down(int x) {
|
|||
|
if (add[x]) {
|
|||
|
update_add(ch[x][0], add[x]);
|
|||
|
update_add(ch[x][1], add[x]);
|
|||
|
add[x] = 0;
|
|||
|
}
|
|||
|
if (rev[x]) {
|
|||
|
update_rev(ch[x][0]);
|
|||
|
update_rev(ch[x][1]);
|
|||
|
rev[x] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void build(int &x, int l, int r, int f) {
|
|||
|
int m = l + r >> 1;
|
|||
|
new_node(x, num[m], f);
|
|||
|
if (l < m) {
|
|||
|
build(ch[x][0], l, m - 1, x);
|
|||
|
}
|
|||
|
if (r > m) {
|
|||
|
build(ch[x][1], m + 1, r, x);
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void init(int n) {
|
|||
|
root = tot1 = tot2 = 0;
|
|||
|
ch[0][0] = ch[0][1] = pre[0] = size[0] = 0;
|
|||
|
add[0] = rev[0] = 0;
|
|||
|
key[0] = vmin[0] = INF;
|
|||
|
new_node(root, -1, 0);
|
|||
|
new_node(ch[root][1], -1, root);
|
|||
|
size[root] = 2;
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &num[i]);
|
|||
|
}
|
|||
|
build(keyTree, 1, n, ch[root][1]);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
void plus(int l, int r, int v) {
|
|||
|
rotate_to(l - 1, 0);
|
|||
|
rotate_to(r + 1, root);
|
|||
|
update_add(keyTree, v);
|
|||
|
}
|
|||
|
void reverse(int l, int r) {
|
|||
|
rotate_to(l - 1, 0);
|
|||
|
rotate_to(r + 1, root);
|
|||
|
update_rev(keyTree);
|
|||
|
}
|
|||
|
void revolve(int l, int r, int k) {
|
|||
|
k %= r - l + 1;
|
|||
|
if (!k) {
|
|||
|
return;
|
|||
|
}
|
|||
|
rotate_to(r - k, 0);
|
|||
|
rotate_to(r + 1, root);
|
|||
|
int tmp = keyTree;
|
|||
|
keyTree = 0;
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
rotate_to(l - 1, 0);
|
|||
|
rotate_to(l, root);
|
|||
|
keyTree = tmp;
|
|||
|
pre[tmp] = ch[root][1];
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
void insert(int k, int v) {
|
|||
|
rotate_to(k, 0);
|
|||
|
rotate_to(k + 1, root);
|
|||
|
new_node(keyTree, v, ch[root][1]);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
void del(int k) {
|
|||
|
rotate_to(k - 1, 0);
|
|||
|
rotate_to(k + 1, root);
|
|||
|
erase(keyTree);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
int query(int l, int r) {
|
|||
|
rotate_to(l - 1, 0);
|
|||
|
rotate_to(r + 1, root);
|
|||
|
return vmin[keyTree];
|
|||
|
}
|
|||
|
} splay;
|
|||
|
int main() {
|
|||
|
int n, m, x, y, v;
|
|||
|
char op[10];
|
|||
|
while (~scanf("%d", &n)) {
|
|||
|
splay.init(n);
|
|||
|
scanf("%d", &m);
|
|||
|
while (m--) {
|
|||
|
scanf("%s", op);
|
|||
|
switch (op[0]) {
|
|||
|
case 'A':
|
|||
|
scanf("%d%d%d", &x, &y, &v);
|
|||
|
splay.plus(x, y, v);
|
|||
|
break;
|
|||
|
case 'R':
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
if (op[3] == 'E') {
|
|||
|
splay.reverse(x, y);
|
|||
|
} else {
|
|||
|
scanf("%d", &v);
|
|||
|
splay.revolve(x, y, v);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 'I':
|
|||
|
scanf("%d%d", &x, &v);
|
|||
|
splay.insert(x, v);
|
|||
|
break;
|
|||
|
case 'D':
|
|||
|
scanf("%d", &x);
|
|||
|
splay.del(x);
|
|||
|
break;
|
|||
|
case 'M':
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
printf("%d\n", splay.query(x, y));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//ver.2
|
|||
|
int k1, k2, num[N];
|
|||
|
struct Splay {
|
|||
|
int root, tot, point;
|
|||
|
int ch[N][2], pre[N], size[N];
|
|||
|
int key[N], add[N], rev[N];
|
|||
|
bool isroot(int x) { return !pre[x] || ch[pre[x]][0] != x && ch[pre[x]][1] != x; }
|
|||
|
void rotate(int x) {
|
|||
|
int y = pre[x], f = ch[y][1] == x;
|
|||
|
ch[y][f] = ch[x][f ^ 1];
|
|||
|
pre[ch[y][f]] = y;
|
|||
|
if (!isroot(y)) { ch[pre[y]][ch[pre[y]][1] == y] = x; }
|
|||
|
pre[x] = pre[y];
|
|||
|
ch[x][f ^ 1] = y;
|
|||
|
pre[y] = x;
|
|||
|
push_up(y);
|
|||
|
}
|
|||
|
void splay(int x) {
|
|||
|
push_down(x);
|
|||
|
while (!isroot(x)) {
|
|||
|
int y = pre[x], z = pre[y];
|
|||
|
if (isroot(y)) {
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
rotate(x);
|
|||
|
} else {
|
|||
|
push_down(z);
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
rotate((ch[z][1] == y) == (ch[y][1] == x) ? y : x);
|
|||
|
rotate(x);
|
|||
|
}
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void new_node(int &x, int v, int fa) {
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
pre[x] = fa;
|
|||
|
size[x] = 1;
|
|||
|
key[x] = v;
|
|||
|
add[x] = rev[x] = 0;
|
|||
|
}
|
|||
|
void update_add(int x, int v) {
|
|||
|
if (x) { key[x] += v; add[x] += v; }
|
|||
|
}
|
|||
|
void update_rev(int x) {
|
|||
|
if (x) { rev[x] ^= 1; swap(ch[x][0], ch[x][1]); }
|
|||
|
}
|
|||
|
void push_down(int x) {
|
|||
|
if (add[x]) {
|
|||
|
update_add(ch[x][0], add[x]);
|
|||
|
update_add(ch[x][1], add[x]);
|
|||
|
add[x] = 0;
|
|||
|
}
|
|||
|
if (rev[x]) {
|
|||
|
update_rev(ch[x][0]);
|
|||
|
update_rev(ch[x][1]);
|
|||
|
rev[x] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void push_up(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + 1; }
|
|||
|
void build(int &x, int l, int r, int fa) {
|
|||
|
int m = l + r >> 1;
|
|||
|
new_node(x, num[m], fa);
|
|||
|
if (l < m) { build(ch[x][0], l, m - 1, x); }
|
|||
|
if (r > m) { build(ch[x][1], m + 1, r, x); }
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void init(int n) {
|
|||
|
root = tot = size[0] = 0;
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &num[i]);
|
|||
|
}
|
|||
|
build(root, 1, n, 0);
|
|||
|
point = 1;
|
|||
|
}
|
|||
|
int find(int rt, int k) {
|
|||
|
int x = rt;
|
|||
|
while (size[ch[x][0]] + 1 != k) {
|
|||
|
push_down(x);
|
|||
|
if (k <= size[ch[x][0]]) {
|
|||
|
x = ch[x][0];
|
|||
|
} else {
|
|||
|
k -= size[ch[x][0]] + 1;
|
|||
|
x = ch[x][1];
|
|||
|
}
|
|||
|
}
|
|||
|
return x;
|
|||
|
}
|
|||
|
void split(int &x, int &y, int sz) {
|
|||
|
if (!sz) { y = x; x = 0; return; }
|
|||
|
y = find(x, sz + 1);
|
|||
|
splay(y);
|
|||
|
x = ch[y][0];
|
|||
|
ch[y][0] = 0;
|
|||
|
push_up(y);
|
|||
|
}
|
|||
|
void split3(int &x, int &y, int &z, int l, int r) {
|
|||
|
split(x, z, r); split(x, y, l - 1);
|
|||
|
}
|
|||
|
void join(int &x, int &y) {
|
|||
|
if (!x || !y) { x |= y; return; }
|
|||
|
x = find(x, size[x]);
|
|||
|
splay(x);
|
|||
|
ch[x][1] = y;
|
|||
|
pre[y] = x;
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void join3(int &x, int y, int z) {
|
|||
|
join(y, z); join(x, y);
|
|||
|
}
|
|||
|
void evert() {
|
|||
|
if (point > 1) {
|
|||
|
int x;
|
|||
|
split(root, x, point - 1);
|
|||
|
swap(root, x);
|
|||
|
join(root, x);
|
|||
|
point = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
void plus(int v) {
|
|||
|
evert();
|
|||
|
int x, y;
|
|||
|
split3(root, x, y, point, point + k2 - 1);
|
|||
|
update_add(x, v);
|
|||
|
join3(root, x, y);
|
|||
|
}
|
|||
|
void reverse() {
|
|||
|
evert();
|
|||
|
int x, y;
|
|||
|
split3(root, x, y, point, point + k1 - 1);
|
|||
|
update_rev(x);
|
|||
|
join3(root, x, y);
|
|||
|
}
|
|||
|
void insert(int v) {
|
|||
|
evert();
|
|||
|
int x, y;
|
|||
|
split(root, x, point);
|
|||
|
new_node(y, v, 0);
|
|||
|
join3(root, y, x);
|
|||
|
}
|
|||
|
void erase() {
|
|||
|
evert();
|
|||
|
int x, y;
|
|||
|
split3(root, x, y, point, point);
|
|||
|
join(root, y);
|
|||
|
}
|
|||
|
void move(int tag) {
|
|||
|
switch (tag) {
|
|||
|
case 1:
|
|||
|
if (--point == 0) { point = size[root]; }
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
if (++point == size[root] + 1) { point = 1; }
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
void query() {
|
|||
|
evert();
|
|||
|
int x, y;
|
|||
|
split3(root, x, y, point, point);
|
|||
|
printf("%d\n", key[x]);
|
|||
|
join3(root, x, y);
|
|||
|
}
|
|||
|
} splay;
|
|||
|
//HDU4453
|
|||
|
int main() {
|
|||
|
int n, m, v, cas = 0;
|
|||
|
char op[10];
|
|||
|
while (~scanf("%d%d%d%d", &n, &m, &k1, &k2) && (n || m || k1 || k2)) {
|
|||
|
splay.init(n);
|
|||
|
printf("Case #%d:\n", ++cas);
|
|||
|
while (m--) {
|
|||
|
scanf("%s", op);
|
|||
|
switch (op[0]) {
|
|||
|
case 'a':
|
|||
|
scanf("%d", &v);
|
|||
|
splay.plus(v);
|
|||
|
break;
|
|||
|
case 'r':
|
|||
|
splay.reverse();
|
|||
|
break;
|
|||
|
case 'i':
|
|||
|
scanf("%d", &v);
|
|||
|
splay.insert(v);
|
|||
|
break;
|
|||
|
case 'd':
|
|||
|
splay.erase();
|
|||
|
break;
|
|||
|
case 'm':
|
|||
|
scanf("%d", &v);
|
|||
|
splay.move(v);
|
|||
|
break;
|
|||
|
case 'q':
|
|||
|
splay.query();
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//ver.3
|
|||
|
const int N = 500005;
|
|||
|
const int INF = 0x3f3f3f3f;
|
|||
|
int n, q;
|
|||
|
struct Splay {
|
|||
|
int pre[N], ch[N][2], key[N], size[N];
|
|||
|
int root, tot1;
|
|||
|
int sum[N], rev[N], same[N];
|
|||
|
int lx[N], rx[N], mx[N];
|
|||
|
int s[N], tot2; //内存池和容量
|
|||
|
int a[N];
|
|||
|
void NewNode(int &r, int father, int k) {
|
|||
|
if (tot2) { r = s[tot2--]; } //取的时候是tot2--,存的时候就是++tot2
|
|||
|
else { r = ++tot1; }
|
|||
|
pre[r] = father;
|
|||
|
ch[r][0] = ch[r][1] = 0;
|
|||
|
key[r] = k;
|
|||
|
sum[r] = k;
|
|||
|
rev[r] = same[r] = 0;
|
|||
|
lx[r] = rx[r] = mx[r] = k;
|
|||
|
size[r] = 1;
|
|||
|
}
|
|||
|
void Update_Rev(int r) {
|
|||
|
if (!r) { return; }
|
|||
|
swap(ch[r][0], ch[r][1]);
|
|||
|
swap(lx[r], rx[r]);
|
|||
|
rev[r] ^= 1;
|
|||
|
}
|
|||
|
void Update_Same(int r, int v) {
|
|||
|
if (!r) { return; }
|
|||
|
key[r] = v;
|
|||
|
sum[r] = v * size[r];
|
|||
|
lx[r] = rx[r] = mx[r] = max(v, v * size[r]);
|
|||
|
same[r] = 1;
|
|||
|
}
|
|||
|
void push_up(int r) {
|
|||
|
int lson = ch[r][0], rson = ch[r][1];
|
|||
|
size[r] = size[lson] + size[rson] + 1;
|
|||
|
sum[r] = sum[lson] + sum[rson] + key[r];
|
|||
|
lx[r] = max(lx[lson], sum[lson] + key[r] + max(0, lx[rson]));
|
|||
|
rx[r] = max(rx[rson], sum[rson] + key[r] + max(0, rx[lson]));
|
|||
|
mx[r] = max(0, rx[lson]) + key[r] + max(0, lx[rson]);
|
|||
|
mx[r] = max(mx[r], max(mx[lson], mx[rson]));
|
|||
|
}
|
|||
|
void push_down(int r) {
|
|||
|
if (same[r]) {
|
|||
|
Update_Same(ch[r][0], key[r]);
|
|||
|
Update_Same(ch[r][1], key[r]);
|
|||
|
same[r] = 0;
|
|||
|
}
|
|||
|
if (rev[r]) {
|
|||
|
Update_Rev(ch[r][0]);
|
|||
|
Update_Rev(ch[r][1]);
|
|||
|
rev[r] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void Build(int &x, int l, int r, int father) {
|
|||
|
if (l > r) { return; }
|
|||
|
int mid = (l + r) / 2;
|
|||
|
NewNode(x, father, a[mid]);
|
|||
|
Build(ch[x][0], l, mid - 1, x);
|
|||
|
Build(ch[x][1], mid + 1, r, x);
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void Init() {
|
|||
|
root = tot1 = tot2 = 0;
|
|||
|
ch[root][0] = ch[root][1] = size[root] = pre[root] = 0;
|
|||
|
same[root] = rev[root] = sum[root] = key[root] = 0;
|
|||
|
lx[root] = rx[root] = mx[root] = -INF;
|
|||
|
NewNode(root, 0, -1);
|
|||
|
NewNode(ch[root][1], root, -1);
|
|||
|
for (int i = 0; i < n; i++) {
|
|||
|
scanf("%d", &a[i]);
|
|||
|
}
|
|||
|
Build(ch[ch[root][1]][0], 0, n - 1, ch[root][1]);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
//旋转,0为左旋, 1为右旋
|
|||
|
void Rotate(int x, int kind) {
|
|||
|
int y = pre[x];
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
ch[y][!kind] = ch[x][kind];
|
|||
|
pre[ch[x][kind]] = y;
|
|||
|
if (pre[y]) { ch[pre[y]][ch[pre[y]][1] == y] = x; }
|
|||
|
pre[x] = pre[y];
|
|||
|
ch[x][kind] = y;
|
|||
|
pre[y] = x;
|
|||
|
push_up(y);
|
|||
|
}
|
|||
|
//Splay调整, 将r结点调整到goal下面
|
|||
|
void Splay(int r, int goal) {
|
|||
|
push_down(r);
|
|||
|
while (pre[r] != goal) {
|
|||
|
if (pre[pre[r]] == goal) {
|
|||
|
push_down(pre[r]);
|
|||
|
push_down(r);
|
|||
|
Rotate(r, ch[pre[r]][0] == r);
|
|||
|
} else {
|
|||
|
push_down(pre[pre[r]]);
|
|||
|
push_down(pre[r]);
|
|||
|
push_down(r);
|
|||
|
int y = pre[r];
|
|||
|
int kind = ch[pre[y]][0] == y;
|
|||
|
if (ch[y][kind] == r) {
|
|||
|
Rotate(r, !kind);
|
|||
|
Rotate(r, kind);
|
|||
|
} else {
|
|||
|
Rotate(y, kind);
|
|||
|
Rotate(r, kind);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
push_up(r);
|
|||
|
if (goal == 0) { root = r; }
|
|||
|
}
|
|||
|
int Get_kth(int r, int k) {
|
|||
|
push_down(r);
|
|||
|
int t = size[ch[r][0]] + 1;
|
|||
|
if (t == k) { return r; }
|
|||
|
if (t > k) { return Get_kth(ch[r][0], k); }
|
|||
|
else { return Get_kth(ch[r][1], k - t); }
|
|||
|
}
|
|||
|
//在第pos个数后面插入tot个数
|
|||
|
void Insert(int pos, int tot) {
|
|||
|
for (int i = 0; i < tot; i++) { scanf("%d", &a[i]); }
|
|||
|
Splay(Get_kth(root, pos + 1), 0);
|
|||
|
Splay(Get_kth(root, pos + 2), root);
|
|||
|
Build(ch[ch[root][1]][0], 0, tot - 1, ch[root][1]);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
//删除子树
|
|||
|
void erase(int r) {
|
|||
|
if (!r) { return; }
|
|||
|
s[++tot2] = r;
|
|||
|
erase(ch[r][0]);
|
|||
|
erase(ch[r][1]);
|
|||
|
}
|
|||
|
//从第pos个数开始连续删除tot个数
|
|||
|
void Delete(int pos, int tot) {
|
|||
|
Splay(Get_kth(root, pos), 0);
|
|||
|
Splay(Get_kth(root, pos + tot + 1), root);
|
|||
|
erase(ch[ch[root][1]][0]);
|
|||
|
pre[ch[ch[root][1]][0]] = 0;
|
|||
|
ch[ch[root][1]][0] = 0;
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
//将从第pos个数开始的连续的tot个数修改为c
|
|||
|
void Make_Same(int pos, int tot, int c) {
|
|||
|
Splay(Get_kth(root, pos), 0);
|
|||
|
Splay(Get_kth(root, pos + tot + 1), root);
|
|||
|
Update_Same(ch[ch[root][1]][0], c);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
//将第pos个数开始的连续tot个数进行反转
|
|||
|
void Reverse(int pos, int tot) {
|
|||
|
Splay(Get_kth(root, pos), 0);
|
|||
|
Splay(Get_kth(root, pos + tot + 1), root);
|
|||
|
Update_Rev(ch[ch[root][1]][0]);
|
|||
|
push_up(ch[root][1]);
|
|||
|
push_up(root);
|
|||
|
}
|
|||
|
//得到第pos个数开始的tot个数的和
|
|||
|
int Get_Sum(int pos, int tot) {
|
|||
|
Splay(Get_kth(root, pos), 0);
|
|||
|
Splay(Get_kth(root, pos + tot + 1), root);
|
|||
|
return sum[ch[ch[root][1]][0]];
|
|||
|
}
|
|||
|
//得到第pos个数开始的tot个数中最大的子段和
|
|||
|
int Get_MaxSum(int pos, int tot) {
|
|||
|
Splay(Get_kth(root, pos), 0);
|
|||
|
Splay(Get_kth(root, pos + tot + 1), root);
|
|||
|
return mx[ch[ch[root][1]][0]];
|
|||
|
}
|
|||
|
void InOrder(int r) {
|
|||
|
if (!r) { return; }
|
|||
|
push_down(r);
|
|||
|
InOrder(ch[r][0]);
|
|||
|
printf("%d ", key[r]);
|
|||
|
InOrder(ch[r][1]);
|
|||
|
}
|
|||
|
} splay;
|
|||
|
int main() {
|
|||
|
while (scanf("%d%d", &n, &q) == 2) {
|
|||
|
splay.Init();
|
|||
|
char op[20];
|
|||
|
int x, y, z;
|
|||
|
while (q--) {
|
|||
|
scanf("%s", op);
|
|||
|
if (strcmp(op, "INSERT") == 0) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
splay.Insert(x, y);
|
|||
|
} else if (strcmp(op, "DELETE") == 0) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
splay.Delete(x, y);
|
|||
|
} else if (strcmp(op, "MAKE-SAME") == 0) {
|
|||
|
scanf("%d%d%d", &x, &y, &z);
|
|||
|
splay.Make_Same(x, y, z);
|
|||
|
} else if (strcmp(op, "REVERSE") == 0) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
splay.Reverse(x, y);
|
|||
|
} else if (strcmp(op, "GET-SUM") == 0) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
printf("%d\n", splay.Get_Sum(x, y));
|
|||
|
} else if (strcmp(op, "MAX-SUM") == 0) {
|
|||
|
printf("%d\n", splay.Get_MaxSum(1, splay.size[root] - 2));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//Link-Cut Tree 动态树
|
|||
|
//维护多棵树(森林)的形态, 并在O(logn)的时间复杂度内维护链上信息; 但LCT处理子树信息将会非常麻烦.
|
|||
|
//它的核心操作是access函数, 可以把某个节点到根的路径上所有点按照深度用Splay维护起来,
|
|||
|
//从而结合evert函数(换跟操作)和splay操作可以实现对链的信息维护.
|
|||
|
//LCT几乎可以实现除维护子树信息外以上的所有操作, 同时有着优越的理论复杂度,
|
|||
|
//但实际常数较大, 很多不改变树形态的题用O(logn)的LCT并不比O(log^2n)的树链剖分套线段树更优越
|
|||
|
struct LCT {
|
|||
|
int ch[N][2], pre[N], key[N], rev[N];
|
|||
|
int add[N], vmax[N];
|
|||
|
bool isroot(int x) { return !pre[x] || ch[pre[x]][0] != x && ch[pre[x]][1] != x; }
|
|||
|
void rotate(int x) {
|
|||
|
int y = pre[x], f = ch[y][1] == x;
|
|||
|
ch[y][f] = ch[x][f ^ 1];
|
|||
|
pre[ch[y][f]] = y;
|
|||
|
if (!isroot(y)) { ch[pre[y]][ch[pre[y]][1] == y] = x; }
|
|||
|
pre[x] = pre[y];
|
|||
|
ch[x][f ^ 1] = y;
|
|||
|
pre[y] = x;
|
|||
|
push_up(y);
|
|||
|
}
|
|||
|
void splay(int x) {
|
|||
|
push_down(x);
|
|||
|
while (!isroot(x)) {
|
|||
|
int y = pre[x], z = pre[y];
|
|||
|
if (isroot(y)) {
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
rotate(x);
|
|||
|
} else {
|
|||
|
push_down(z);
|
|||
|
push_down(y);
|
|||
|
push_down(x);
|
|||
|
rotate((ch[z][1] == y) == (ch[y][1] == x) ? y : x);
|
|||
|
rotate(x);
|
|||
|
}
|
|||
|
}
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
int access(int x) {
|
|||
|
int y = 0;
|
|||
|
for (; x; x = pre[x]) {
|
|||
|
splay(x);
|
|||
|
ch[x][1] = y;
|
|||
|
push_up(x);
|
|||
|
y = x;
|
|||
|
}
|
|||
|
return y;
|
|||
|
}
|
|||
|
void evert(int x) {
|
|||
|
rev[access(x)] ^= 1;
|
|||
|
splay(x);
|
|||
|
}
|
|||
|
void push_up(int x) { vmax[x] = max(max(vmax[ch[x][0]], vmax[ch[x][1]]), key[x]); }
|
|||
|
void push_down(int x) {
|
|||
|
if (add[x]) {
|
|||
|
key[x] += add[x];
|
|||
|
if (ch[x][0]) {
|
|||
|
add[ch[x][0]] += add[x];
|
|||
|
vmax[ch[x][0]] += add[x];
|
|||
|
}
|
|||
|
if (ch[x][1]) {
|
|||
|
add[ch[x][1]] += add[x];
|
|||
|
vmax[ch[x][1]] += add[x];
|
|||
|
}
|
|||
|
add[x] = 0;
|
|||
|
}
|
|||
|
if (rev[x]) {
|
|||
|
if (ch[x][0]) { rev[ch[x][0]] ^= 1; }
|
|||
|
if (ch[x][1]) { rev[ch[x][1]] ^= 1; }
|
|||
|
swap(ch[x][0], ch[x][1]);
|
|||
|
rev[x] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
int find_root(int x) {
|
|||
|
while (pre[x]) { x = pre[x]; }
|
|||
|
return x;
|
|||
|
}
|
|||
|
//如果u,v不在同一颗子树中,则通过在u,v之间连边的方式,连接这两颗子树
|
|||
|
void link(int u, int v) {
|
|||
|
if (find_root(u) == find_root(v)) { puts("-1"); return; }
|
|||
|
evert(u);
|
|||
|
pre[u] = v;
|
|||
|
}
|
|||
|
//如果u,v在同一颗子树中,且u!=v,则将u视为这颗子树的根以后,切断v与其父亲结点的连接
|
|||
|
void cut(int u, int v) {
|
|||
|
if (u == v || find_root(u) != find_root(v)) { puts("-1"); return; }
|
|||
|
evert(u);
|
|||
|
access(v);
|
|||
|
splay(v);
|
|||
|
pre[ch[v][0]] = 0;
|
|||
|
ch[v][0] = 0;
|
|||
|
push_up(v);
|
|||
|
}
|
|||
|
//如果u,v在同一颗子树中,则将u,v之间路径上所有点的点权增加w
|
|||
|
void update(int u, int v, int w) {
|
|||
|
if (find_root(u) != find_root(v)) { puts("-1"); return; }
|
|||
|
evert(u);
|
|||
|
access(v);
|
|||
|
splay(v);
|
|||
|
add[v] += w;
|
|||
|
vmax[v] += w;
|
|||
|
push_down(v);
|
|||
|
}
|
|||
|
//如果u,v在同一颗子树中, 返回u,v之间路径上点权的最大值
|
|||
|
void query(int u, int v) {
|
|||
|
if (find_root(u) != find_root(v)) { puts("-1"); return; }
|
|||
|
evert(u);
|
|||
|
access(v);
|
|||
|
splay(v);
|
|||
|
printf("%d\n", vmax[v]);
|
|||
|
}
|
|||
|
struct graph {
|
|||
|
int head[N], to[N << 1], next[N << 1];
|
|||
|
int tot;
|
|||
|
void init() { tot = 0; memset(head, 0xff, sizeof(head)); }
|
|||
|
void add(int u, int v) {
|
|||
|
to[tot] = v;
|
|||
|
next[tot] = head[u];
|
|||
|
head[u] = tot++;
|
|||
|
}
|
|||
|
} g;
|
|||
|
void dfs(int u, int fa) {
|
|||
|
for (int i = g.head[u]; ~i; i = g.next[i]) {
|
|||
|
int v = g.to[i];
|
|||
|
if (v != fa) {
|
|||
|
dfs(v, u);
|
|||
|
pre[v] = u;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
void init(int n) {
|
|||
|
int m, x, y;
|
|||
|
g.init();
|
|||
|
for (int i = 1; i < n; ++i) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
g.add(x, y); g.add(y, x);
|
|||
|
}
|
|||
|
memset(ch, 0, sizeof(ch));
|
|||
|
memset(pre, 0, sizeof(pre));
|
|||
|
memset(rev, 0, sizeof(rev));
|
|||
|
memset(add, 0, sizeof(add));
|
|||
|
vmax[0] = 0;
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &key[i]);
|
|||
|
vmax[i] = key[i];
|
|||
|
}
|
|||
|
dfs(1, 0);
|
|||
|
}
|
|||
|
} lct;
|
|||
|
//HDU4010
|
|||
|
int main() {
|
|||
|
int n, q, op, x, y, w;
|
|||
|
while (~scanf("%d", &n)) {
|
|||
|
lct.init(n);
|
|||
|
scanf("%d", &q);
|
|||
|
while (q--) {
|
|||
|
scanf("%d", &op);
|
|||
|
switch (op) {
|
|||
|
case 1:
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
lct.link(x, y);
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
lct.cut(x, y);
|
|||
|
break;
|
|||
|
case 3:
|
|||
|
scanf("%d%d%d", &w, &x, &y);
|
|||
|
lct.update(x, y, w);
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
lct.query(x, y);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
putchar('\n');
|
|||
|
}
|
|||
|
}
|
|||
|
//不基于旋转的Treap
|
|||
|
int num[N];
|
|||
|
struct Treap {
|
|||
|
int tot, root;
|
|||
|
int ch[N][2], pt[N], size[N];
|
|||
|
int key[N], vmin[N], add[N], rev[N];
|
|||
|
void init() { tot = 0; }
|
|||
|
void new_node(int &x, int v) {
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
size[x] = 1;
|
|||
|
pt[x] = rand();
|
|||
|
key[x] = vmin[x] = v;
|
|||
|
add[x] = rev[x] = 0;
|
|||
|
}
|
|||
|
void merge(int &p, int x, int y) {
|
|||
|
if (!x || !y) { p = x | y; return; }
|
|||
|
if (pt[x] < pt[y]) {
|
|||
|
push_down(x);
|
|||
|
merge(ch[x][1], ch[x][1], y);
|
|||
|
p = x;
|
|||
|
} else {
|
|||
|
push_down(y);
|
|||
|
merge(ch[y][0], x, ch[y][0]);
|
|||
|
p = y;
|
|||
|
}
|
|||
|
push_up(p);
|
|||
|
}
|
|||
|
void split(int p, int sz, int &x, int &y) {
|
|||
|
if (!sz) { x = 0; y = p; return; }
|
|||
|
push_down(p);
|
|||
|
if (size[ch[p][0]] >= sz) {
|
|||
|
y = p;
|
|||
|
split(ch[p][0], sz, x, ch[y][0]);
|
|||
|
} else {
|
|||
|
x = p;
|
|||
|
split(ch[p][1], sz - size[ch[p][0]] - 1, ch[x][1], y);
|
|||
|
}
|
|||
|
push_up(p);
|
|||
|
}
|
|||
|
void update_add(int x, int v) {
|
|||
|
if (x) { key[x] += v; add[x] += v; vmin[x] += v; }
|
|||
|
}
|
|||
|
void update_rev(int x) {
|
|||
|
if (x) { swap(ch[x][0], ch[x][1]); rev[x] ^= 1; }
|
|||
|
}
|
|||
|
void push_down(int x) {
|
|||
|
if (add[x]) {
|
|||
|
update_add(ch[x][0], add[x]);
|
|||
|
update_add(ch[x][1], add[x]);
|
|||
|
add[x] = 0;
|
|||
|
}
|
|||
|
if (rev[x]) {
|
|||
|
update_rev(ch[x][0]);
|
|||
|
update_rev(ch[x][1]);
|
|||
|
rev[x] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void push_up(int x) {
|
|||
|
size[x] = 1;
|
|||
|
vmin[x] = key[x];
|
|||
|
if (ch[x][0]) {
|
|||
|
size[x] += size[ch[x][0]];
|
|||
|
vmin[x] = min(vmin[x], vmin[ch[x][0]]);
|
|||
|
}
|
|||
|
if (ch[x][1]) {
|
|||
|
size[x] += size[ch[x][1]];
|
|||
|
vmin[x] = min(vmin[x], vmin[ch[x][1]]);
|
|||
|
}
|
|||
|
}
|
|||
|
int build(int &x, int l, int r) {
|
|||
|
int m = l + r >> 1;
|
|||
|
new_node(x, num[m]);
|
|||
|
if (l < m) { build(ch[x][0], l, m - 1); }
|
|||
|
if (r > m) { build(ch[x][1], m + 1, r); }
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void plus(int l, int r, int v) {
|
|||
|
int x, y;
|
|||
|
split(root, l - 1, root, x);
|
|||
|
split(x, r - l + 1, x, y);
|
|||
|
update_add(x, v);
|
|||
|
merge(x, x, y);
|
|||
|
merge(root, root, x);
|
|||
|
}
|
|||
|
void reverse(int l, int r) {
|
|||
|
int x, y;
|
|||
|
split(root, l - 1, root, x);
|
|||
|
split(x, r - l + 1, x, y);
|
|||
|
update_rev(x);
|
|||
|
merge(x, x, y);
|
|||
|
merge(root, root, x);
|
|||
|
}
|
|||
|
void revolve(int l, int r, int k) {
|
|||
|
int x, y, p, q;
|
|||
|
k %= r - l + 1;
|
|||
|
if (!k) { return; }
|
|||
|
split(root, l - 1, root, x);
|
|||
|
split(x, r - l + 1, x, y);
|
|||
|
split(x, r - l + 1 - k, p, q);
|
|||
|
merge(x, q, p);
|
|||
|
merge(x, x, y);
|
|||
|
merge(root, root, x);
|
|||
|
}
|
|||
|
void insert(int k, int v) {
|
|||
|
int x, y;
|
|||
|
new_node(x, v);
|
|||
|
split(root, k, root, y);
|
|||
|
merge(root, root, x);
|
|||
|
merge(root, root, y);
|
|||
|
}
|
|||
|
void erase(int k) {
|
|||
|
int x, y;
|
|||
|
split(root, k - 1, root, x);
|
|||
|
split(x, 1, x, y);
|
|||
|
merge(root, root, y);
|
|||
|
}
|
|||
|
int query(int l, int r) {
|
|||
|
int x, y, ret;
|
|||
|
split(root, l - 1, root, x);
|
|||
|
split(x, r - l + 1, x, y);
|
|||
|
ret = vmin[x];
|
|||
|
merge(x, x, y);
|
|||
|
merge(root, root, x);
|
|||
|
return ret;
|
|||
|
}
|
|||
|
} treap;
|
|||
|
//POJ3580
|
|||
|
int main() {
|
|||
|
int n, m, x, y, v;
|
|||
|
char op[10];
|
|||
|
while (~scanf("%d", &n)) {
|
|||
|
treap.init();
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &num[i]);
|
|||
|
}
|
|||
|
treap.build(treap.root, 1, n);
|
|||
|
scanf("%d", &m);
|
|||
|
while (m--) {
|
|||
|
scanf("%s", op);
|
|||
|
switch (op[0]) {
|
|||
|
case 'A':
|
|||
|
scanf("%d%d%d", &x, &y, &v);
|
|||
|
treap.plus(x, y, v);
|
|||
|
break;
|
|||
|
case 'R':
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
if (op[3] == 'E') {
|
|||
|
treap.reverse(x, y);
|
|||
|
} else {
|
|||
|
scanf("%d", &v);
|
|||
|
treap.revolve(x, y, v);
|
|||
|
}
|
|||
|
break;
|
|||
|
case 'I':
|
|||
|
scanf("%d%d", &x, &v);
|
|||
|
treap.insert(x, v);
|
|||
|
break;
|
|||
|
case 'D':
|
|||
|
scanf("%d", &x);
|
|||
|
treap.erase(x);
|
|||
|
break;
|
|||
|
case 'M':
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
printf("%d\n", treap.query(x, y));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//可持久化Treap
|
|||
|
const int N = 50005;
|
|||
|
const int M = 5000005;
|
|||
|
int root[N], vs, d;
|
|||
|
struct Treap {
|
|||
|
int tot;
|
|||
|
int ch[M][2], size[M];
|
|||
|
char key[M];
|
|||
|
bool hey(int x, int y) { return (ll)rand() * (size[x] + size[y]) < (ll)size[x] * RAND_MAX; }
|
|||
|
void init() { tot = 0; }
|
|||
|
void new_node(int &x, char v) {
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[x][1] = 0;
|
|||
|
size[x] = 1;
|
|||
|
key[x] = v;
|
|||
|
}
|
|||
|
void copy_node(int &x, int y) {
|
|||
|
if (!y) { x = 0; return; }
|
|||
|
x = ++tot;
|
|||
|
ch[x][0] = ch[y][0];
|
|||
|
ch[x][1] = ch[y][1];
|
|||
|
size[x] = size[y];
|
|||
|
key[x] = key[y];
|
|||
|
}
|
|||
|
void merge(int &p, int x, int y) {
|
|||
|
if (!x || !y) {
|
|||
|
p = 0;
|
|||
|
if (x) { copy_node(p, x); }
|
|||
|
if (y) { copy_node(p, y); }
|
|||
|
return;
|
|||
|
}
|
|||
|
if (hey(x, y)) {
|
|||
|
copy_node(p, x);
|
|||
|
merge(ch[p][1], ch[x][1], y);
|
|||
|
} else {
|
|||
|
copy_node(p, y);
|
|||
|
merge(ch[p][0], x, ch[y][0]);
|
|||
|
}
|
|||
|
push_up(p);
|
|||
|
}
|
|||
|
void split(int p, int sz, int &x, int &y) {
|
|||
|
if (!sz) { x = 0; copy_node(y, p); return; }
|
|||
|
if (size[ch[p][0]] >= sz) {
|
|||
|
copy_node(y, p);
|
|||
|
split(ch[p][0], sz, x, ch[y][0]);
|
|||
|
push_up(y);
|
|||
|
} else {
|
|||
|
copy_node(x, p);
|
|||
|
split(ch[p][1], sz - size[ch[p][0]] - 1, ch[x][1], y);
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
}
|
|||
|
void push_up(int x) {
|
|||
|
size[x] = 1;
|
|||
|
if (ch[x][0]) { size[x] += size[ch[x][0]]; }
|
|||
|
if (ch[x][1]) { size[x] += size[ch[x][1]]; }
|
|||
|
}
|
|||
|
void build(char str[], int &x, int l, int r) {
|
|||
|
int m = l + r >> 1;
|
|||
|
new_node(x, str[m]);
|
|||
|
if (l < m) { build(str, ch[x][0], l, m - 1); }
|
|||
|
if (r > m) { build(str, ch[x][1], m + 1, r); }
|
|||
|
push_up(x);
|
|||
|
}
|
|||
|
void insert(int k, char str[]) {
|
|||
|
int x, y, z;
|
|||
|
build(str, x, 0, strlen(str) - 1);
|
|||
|
split(root[vs], k, y, z);
|
|||
|
merge(y, y, x);
|
|||
|
merge(root[++vs], y, z);
|
|||
|
}
|
|||
|
void erase(int k, int sz) {
|
|||
|
int x, y, z;
|
|||
|
split(root[vs], k - 1, x, y);
|
|||
|
split(y, sz, y, z);
|
|||
|
merge(root[++vs], x, z);
|
|||
|
}
|
|||
|
void output(int x) {
|
|||
|
if (ch[x][0]) { output(ch[x][0]); }
|
|||
|
putchar(key[x]);
|
|||
|
d += key[x] == 'c';
|
|||
|
if (ch[x][1]) { output(ch[x][1]); }
|
|||
|
}
|
|||
|
void output(int v, int k, int sz) {
|
|||
|
int x, y, z;
|
|||
|
split(root[v], k - 1, x, y);
|
|||
|
split(y, sz, y, z);
|
|||
|
output(y);
|
|||
|
putchar('\n');
|
|||
|
}
|
|||
|
} treap;
|
|||
|
//UVa12538
|
|||
|
int main() {
|
|||
|
int n, op, p, c, v;
|
|||
|
char s[105];
|
|||
|
treap.init();
|
|||
|
vs = d = 0;
|
|||
|
scanf("%d", &n);
|
|||
|
while (n--) {
|
|||
|
scanf("%d", &op);
|
|||
|
switch (op) {
|
|||
|
case 1:
|
|||
|
scanf("%d%s", &p, s);
|
|||
|
treap.insert(p - d, s);
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
scanf("%d%d", &p, &c);
|
|||
|
treap.erase(p - d, c - d);
|
|||
|
break;
|
|||
|
case 3:
|
|||
|
scanf("%d%d%d", &v, &p, &c);
|
|||
|
treap.output(v - d, p - d, c - d);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//树链剖分
|
|||
|
//轻重链剖分将一棵树划分成至多logn条重链和若干条轻边, 满足每个节点属于一条重链,
|
|||
|
//从而将树上路径修改转化为至多logn次线性修改, 非常利于套用树状数组、线段树等各类数据结构.
|
|||
|
//树链剖分的常数很小, 且因着树链剖分的性质, 我们发现越是退化的树(极端情况下成为一条链),
|
|||
|
//树链剖分的效果越是好(极端情况下甚至是O(1)级的, 因为只有很少的重链),
|
|||
|
//以至于一些不涉及形态修改的树上路径维护题目, 可以用树链剖分套线段树以O(logn^2)的单次操作复杂度水过,
|
|||
|
//且实际表现不输于单次操作O(logn)但常数很大的LCT.
|
|||
|
//常见轻重链剖分的初始化实现是两次dfs的, 但dfs有两个问题,
|
|||
|
//一是递归调用使得时间稍慢, 二是有些题目有爆栈风险; 所以我抄了bfs实现的很好用的交大板.
|
|||
|
//需要稍作说明的是, 对于点权修改直接维护即可, 对于边权修改, 常规做法是选定一个根,
|
|||
|
//将边权下垂到深度更大的节点上; 换言之, 每个点储存的权值是它与它的父节点之间的边权, 根节点上没有权值.
|
|||
|
int top[N]; //top[p]表示编号为p的路径的顶端节点
|
|||
|
int len[N]; //len[p]表示路径p的长度
|
|||
|
int belong[N]; //belong[v]表示节点v所属的路径编号
|
|||
|
int idx[N]; //idx[v]表示节点v在其路径中的编号, 按深度由深到浅依次标号
|
|||
|
int dep[N]; //dep[v]表示节点v的深度
|
|||
|
int fa[N]; //fa[v]表示节点v的父亲节点
|
|||
|
int size[N]; //size[v]表示以节点v为根的子树的节点个数
|
|||
|
int que[N];
|
|||
|
bool vis[N];
|
|||
|
int n, cnt; //n是点数, 标号从1到n
|
|||
|
void split() {
|
|||
|
memset(dep, 0xff, sizeof(dep));
|
|||
|
int l = 0, r = 0;
|
|||
|
que[++r] = 1; dep[1] = 0; fa[1] = -1;
|
|||
|
while (l < r) {
|
|||
|
int u = que[++l];
|
|||
|
vis[u] = false;
|
|||
|
for (int i = g.head[u]; ~i; i = g.next[i]) {
|
|||
|
int v = g.to[i];
|
|||
|
if (!~dep[v]) { que[++r] = v; dep[v] = dep[u] + 1; fa[v] = u; }
|
|||
|
}
|
|||
|
}
|
|||
|
cnt = 0;
|
|||
|
for (int i = n; i > 0; --i) {
|
|||
|
int u = que[i], p = -1;
|
|||
|
size[u] = 1;
|
|||
|
for (int j = g.head[u]; ~j; j = g.next[j]) {
|
|||
|
int v = g.to[j];
|
|||
|
if (vis[v]) {
|
|||
|
size[u] += size[v];
|
|||
|
if (!~p || size[v] > size[p]) { p = v; }
|
|||
|
}
|
|||
|
}
|
|||
|
if (!~p) {
|
|||
|
idx[u] = len[++cnt] = 1;
|
|||
|
belong[u] = cnt;
|
|||
|
top[cnt] = u;
|
|||
|
} else {
|
|||
|
belong[u] = belong[p];
|
|||
|
idx[u] = ++len[belong[u]];
|
|||
|
top[belong[u]] = u;
|
|||
|
}
|
|||
|
vis[u] = true;
|
|||
|
}
|
|||
|
}
|
|||
|
int fi[N], cid[N], rank[N];
|
|||
|
void getcid() {
|
|||
|
fi[1] = 1;
|
|||
|
for (int i = 2; i <= cnt; ++i) { fi[i] = fi[i - 1] + len[i - 1]; }
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
cid[i] = fi[belong[i]] + len[belong[i]] - idx[i];
|
|||
|
rank[cid[i]] = i;
|
|||
|
}
|
|||
|
}
|
|||
|
// 路径修改和查询依下面修改
|
|||
|
int query(int x, int y) {
|
|||
|
int ret = 0;
|
|||
|
while (belong[x] != belong[y]) {
|
|||
|
if (dep[top[belong[x]]] < dep[top[belong[y]]]) { swap(x, y); }
|
|||
|
ret = max(ret, query(cid[top[belong[x]]], cid[x], 1, n, 1));
|
|||
|
x = fa[top[belong[x]]];
|
|||
|
}
|
|||
|
if (dep[x] > dep[y]) { swap(x, y); }
|
|||
|
ret = max(ret, query(cid[x], cid[y], 1, n, 1));
|
|||
|
/*边权如下
|
|||
|
if(x!=y)
|
|||
|
ret=max(ret,query(cid[x]+1,cid[y],1,n,1));
|
|||
|
*/
|
|||
|
return ret;
|
|||
|
}
|
|||
|
//第一次dfs和倍增LCA的dfs部分几乎一致; 所以稍作修改就可以无缝衔接LCA.
|
|||
|
//第二次dfs对节点的新位置进行了标号(对应bfs的getcid函数),
|
|||
|
//我们可以发现无论它以怎样的顺序进行dfs(先dfs重儿子再dfs其它子节点), 得到的依旧是这棵树的一个dfs序.
|
|||
|
//换句话说, 这里处理出的剖分标号, 同时也是dfs序标号.
|
|||
|
//我们知道每棵子树的节点在dfs序中都是连续的一段,
|
|||
|
//这样我们就可以同时维护树上路径信息(剖分部分复杂度O(logn))和子树信息(剖分部分复杂度O(1))了.
|
|||
|
//BZOJ3083 树链剖分套线段树
|
|||
|
const int N = 100005;
|
|||
|
const int maxd = 18;
|
|||
|
const int INF = 0x7fffffff;
|
|||
|
struct graph {
|
|||
|
int head[N], tot;
|
|||
|
int to[N << 1], next[N << 1];
|
|||
|
void init() {
|
|||
|
tot = 0; memset(head, 0xff, sizeof(head));
|
|||
|
}
|
|||
|
void add(int u, int v) {
|
|||
|
to[tot] = v; next[tot] = head[u]; head[u] = tot++;
|
|||
|
}
|
|||
|
} g;
|
|||
|
int top[N], son[N];
|
|||
|
int dep[N], fa[N][maxd], size[N];
|
|||
|
int cid[N], rank[N], cnt;
|
|||
|
void dfs1(int u) {
|
|||
|
size[u] = 1; son[u] = -1;
|
|||
|
for (int i = 1; i < maxd; ++i) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; }
|
|||
|
for (int i = g.head[u]; ~i; i = g.next[i]) {
|
|||
|
int v = g.to[i];
|
|||
|
if (v != fa[u][0]) {
|
|||
|
dep[v] = dep[u] + 1; fa[v][0] = u;
|
|||
|
dfs1(v);
|
|||
|
size[u] += size[v];
|
|||
|
if (!~son[u] || size[v] > size[son[u]]) { son[u] = v; }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
void dfs2(int u, int tp) {
|
|||
|
top[u] = tp; cid[u] = ++cnt; rank[cid[u]] = u;
|
|||
|
if (~son[u]) { dfs2(son[u], tp); }
|
|||
|
for (int i = g.head[u]; ~i; i = g.next[i]) {
|
|||
|
int v = g.to[i];
|
|||
|
if (v != son[u] && v != fa[u][0]) { dfs2(v, v); }
|
|||
|
}
|
|||
|
}
|
|||
|
void split() {
|
|||
|
dfs1(1); cnt = 0; dfs2(1, 1);
|
|||
|
}
|
|||
|
int lca(int u, int v) {
|
|||
|
if (dep[u] < dep[v]) { swap(u, v); }
|
|||
|
int k = dep[u] - dep[v];
|
|||
|
for (int i = 0; i < maxd; ++i) {
|
|||
|
if ((1 << i)&k) { u = fa[u][i]; }
|
|||
|
}
|
|||
|
if (u == v) { return u; }
|
|||
|
for (int i = maxd - 1; i >= 0; --i) {
|
|||
|
if (fa[u][i] != fa[v][i]) { u = fa[u][i]; v = fa[v][i]; }
|
|||
|
}
|
|||
|
return fa[u][0];
|
|||
|
}
|
|||
|
int n, root, a[N];
|
|||
|
#define lson l,m,rt<<1
|
|||
|
#define rson m+1,r,rt<<1|1
|
|||
|
int vmin[N << 2], col[N << 2];
|
|||
|
void push_up(int rt) {
|
|||
|
vmin[rt] = min(vmin[rt << 1], vmin[rt << 1 | 1]);
|
|||
|
}
|
|||
|
void push_down(int rt) {
|
|||
|
if (col[rt]) {
|
|||
|
col[rt << 1] = col[rt << 1 | 1] = vmin[rt << 1] = vmin[rt << 1 | 1] = col[rt];
|
|||
|
col[rt] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void build(int l, int r, int rt) {
|
|||
|
col[rt] = 0;
|
|||
|
if (l == r) { vmin[rt] = a[rank[l]]; return; }
|
|||
|
int m = l + r >> 1;
|
|||
|
build(lson);
|
|||
|
build(rson);
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
void update(int L, int R, int val, int l, int r, int rt) {
|
|||
|
if (L <= l && r <= R) { col[rt] = vmin[rt] = val; return; }
|
|||
|
push_down(rt);
|
|||
|
int m = l + r >> 1;
|
|||
|
if (L <= m) { update(L, R, val, lson); }
|
|||
|
if (m < R) { update(L, R, val, rson); }
|
|||
|
push_up(rt);
|
|||
|
}
|
|||
|
int query(int L, int R, int l, int r, int rt) {
|
|||
|
if (L <= l && r <= R) { return vmin[rt]; }
|
|||
|
push_down(rt);
|
|||
|
int m = l + r >> 1;
|
|||
|
int ret = INF;
|
|||
|
if (L <= m) { ret = min(ret, query(L, R, lson)); }
|
|||
|
if (m < R) { ret = min(ret, query(L, R, rson)); }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
void modify(int x, int y, int d) {
|
|||
|
while (top[x] != top[y]) {
|
|||
|
if (dep[top[x]] < dep[top[y]]) { swap(x, y); }
|
|||
|
update(cid[top[x]], cid[x], d, 1, n, 1);
|
|||
|
x = fa[top[x]][0];
|
|||
|
}
|
|||
|
if (dep[x] > dep[y]) { swap(x, y); }
|
|||
|
update(cid[x], cid[y], d, 1, n, 1);
|
|||
|
}
|
|||
|
int query(int rt) {
|
|||
|
if (rt == root) { return query(1, n, 1, n, 1); }
|
|||
|
int pre = lca(root, rt);
|
|||
|
if (pre != rt) { return query(cid[rt], cid[rt] + size[rt] - 1, 1, n, 1); }
|
|||
|
int depth = dep[root] - dep[rt] - 1, tmp = root;
|
|||
|
for (int i = maxd - 1; i >= 0; --i) {
|
|||
|
if (depth & (1 << i)) { tmp = fa[tmp][i]; }
|
|||
|
}
|
|||
|
return min(query(1, cid[tmp] - 1, 1, n, 1), query(cid[tmp] + size[tmp], n, 1, n, 1));
|
|||
|
}
|
|||
|
int main() {
|
|||
|
int m, u, v, opt, id;
|
|||
|
while (~scanf("%d%d", &n, &m)) {
|
|||
|
g.init();
|
|||
|
for (int i = 1; i < n; ++i) {
|
|||
|
scanf("%d%d", &u, &v);
|
|||
|
g.add(u, v); g.add(v, u);
|
|||
|
}
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &a[i]);
|
|||
|
}
|
|||
|
split();
|
|||
|
build(1, n, 1);
|
|||
|
scanf("%d", &root);
|
|||
|
while (m--) {
|
|||
|
scanf("%d", &opt);
|
|||
|
switch (opt) {
|
|||
|
case 1: scanf("%d", &root); break;
|
|||
|
case 2: scanf("%d%d%d", &u, &v, &id); modify(u, v, id); break;
|
|||
|
case 3: scanf("%d", &id); printf("%d\n", query(id)); break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//KD-Tree
|
|||
|
//用来维护多维第K近点对距离一类的信息.
|
|||
|
//在每一维上依次用一个超平面进行空间划分, 将点集比较均匀地分割在各个区域内,
|
|||
|
//结构上则是一棵二叉树, 且与线段树的形态和构造方法都有些类似.
|
|||
|
//经过改造的KD-Tree一般可以做到O(logn)的单点插入, 以及O(n^(1-1/D))的询问操作, 其中D是维数, 可见维数越大KD-Tree越慢
|
|||
|
//询问距离一个点的前K近点一般需要用一个优先队列进行询问时的维护
|
|||
|
//HDU4347 O(n) 不支持点的插入和删除
|
|||
|
const int N = 50005;
|
|||
|
const int INF = ~0U >> 1;
|
|||
|
const int DIM = 5;
|
|||
|
#define lson l,m-1,dep+1
|
|||
|
#define rson m+1,r,dep+1
|
|||
|
int cur, K;
|
|||
|
struct point {
|
|||
|
int x[DIM];
|
|||
|
bool operator<(const point &oth)const { return x[cur] < oth.x[cur]; }
|
|||
|
void output() {
|
|||
|
for (int i = 0; i < K; ++i) {
|
|||
|
printf("%d%c", x[i], i < K - 1 ? ' ' : '\n');
|
|||
|
}
|
|||
|
}
|
|||
|
} vec[N], origin[N], pt, ans[10];
|
|||
|
inline int sqr(int x) { return x * x; }
|
|||
|
int dist(const point &a, const point &b) {
|
|||
|
int ret = 0;
|
|||
|
for (int i = 0; i < K; ++i) { ret += sqr(a.x[i] - b.x[i]); }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
void build(int l, int r, int dep = 0) {
|
|||
|
if (l >= r) { return; }
|
|||
|
int m = l + r >> 1;
|
|||
|
cur = dep % K;
|
|||
|
nth_element(vec + l, vec + m, vec + r + 1);
|
|||
|
build(lson);
|
|||
|
build(rson);
|
|||
|
}
|
|||
|
priority_queue<pair<int, point>> pq;
|
|||
|
void query(const point &x, int k, int l, int r, int dep = 0) {
|
|||
|
if (l > r) { return; }
|
|||
|
int m = l + r >> 1, cur = dep % K;
|
|||
|
pair<int, point> tmp(dist(x, vec[m]), vec[m]);
|
|||
|
if (pq.size() < k) {
|
|||
|
pq.push(tmp);
|
|||
|
} else if (pq.top().first > tmp.first) {
|
|||
|
pq.pop(); pq.push(tmp);
|
|||
|
}
|
|||
|
if (x.x[cur] < vec[m].x[cur]) {
|
|||
|
query(x, k, lson);
|
|||
|
if (pq.top().first > sqr(x.x[cur] - vec[m].x[cur])) { query(x, k, rson); }
|
|||
|
} else {
|
|||
|
query(x, k, rson);
|
|||
|
if (pq.top().first > sqr(x.x[cur] - vec[m].x[cur])) { query(x, k, lson); }
|
|||
|
}
|
|||
|
}
|
|||
|
int main() {
|
|||
|
int n, t, m;
|
|||
|
while (~scanf("%d%d", &n, &K)) {
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
for (int j = 0; j < K; ++j) {
|
|||
|
scanf("%d", &origin[i].x[j]);
|
|||
|
}
|
|||
|
vec[i] = origin[i];
|
|||
|
}
|
|||
|
build(1, n);
|
|||
|
scanf("%d", &t);
|
|||
|
while (t--) {
|
|||
|
for (int i = 0; i < K; ++i) {
|
|||
|
scanf("%d", &pt.x[i]);
|
|||
|
}
|
|||
|
scanf("%d", &m);
|
|||
|
query(pt, m, 1, n);
|
|||
|
for (int i = 0; i < m; ++i) {
|
|||
|
ans[i] = pq.top().second; pq.pop();
|
|||
|
}
|
|||
|
printf("the closest %d points are:\n", m);
|
|||
|
for (int i = m - 1; i >= 0; --i) { ans[i].output(); }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//支持点的插入和删除
|
|||
|
#define lson kdt[rt].ls,dep+1
|
|||
|
#define rson kdt[rt].rs,dep+1
|
|||
|
struct kdnode {
|
|||
|
int ls, rs, x[DIM]; bool flag; //删点标记
|
|||
|
} kdt[N];
|
|||
|
inline ll sqr(int x) { return (ll)x * x; }
|
|||
|
ll dist(const kdnode &a, const kdnode &b) {
|
|||
|
ll ret = 0;
|
|||
|
for (int i = 0; i < DIM; ++i) { ret += sqr(a.x[i] - b.x[i]); }
|
|||
|
return ret;
|
|||
|
}
|
|||
|
int root, tot;
|
|||
|
void init() { tot = 0; root = -1; }
|
|||
|
int add(int pt[]) {
|
|||
|
kdt[tot].flag = false;
|
|||
|
kdt[tot].ls = kdt[tot].rs = -1;
|
|||
|
for (int i = 0; i < DIM; ++i) { kdt[tot].x[i] = pt[i]; }
|
|||
|
return tot++;
|
|||
|
}
|
|||
|
void insert(int pt[], int rt, int dep = 0) {
|
|||
|
dep %= DIM;
|
|||
|
if (pt[dep] < kdt[rt].x[dep]) {
|
|||
|
if (!~kdt[rt].ls) { kdt[rt].ls = add(pt); }
|
|||
|
else { insert(pt, lson); }
|
|||
|
} else {
|
|||
|
if (!~kdt[rt].rs) { kdt[rt].rs = add(pt); }
|
|||
|
else { insert(pt, rson); }
|
|||
|
}
|
|||
|
}
|
|||
|
//求最近点距离
|
|||
|
ll query(const kdnode &pt, int rt, int dep = 0) {
|
|||
|
if (!~rt) { return INF; }
|
|||
|
dep %= DIM;
|
|||
|
ll ret = INF, tmp = sqr(kdt[rt].x[dep] - pt.x[dep]);
|
|||
|
if (!kdt[rt].flag) { ret = dist(kdt[rt], pt); }
|
|||
|
if (pt.x[dep] <= kdt[rt].x[dep]) {
|
|||
|
ret = min(ret, query(pt, lson));
|
|||
|
if (tmp < ret) { ret = min(ret, query(pt, rson)); }
|
|||
|
}
|
|||
|
if (pt.x[dep] >= kdt[rt].x[dep]) {
|
|||
|
ret = min(ret, query(pt, rson));
|
|||
|
if (tmp < ret) { ret = min(ret, query(pt, lson)); }
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
//查询区间内有多少个点
|
|||
|
int query(int pt1[], int pt2[], int rt, int dep = 0) {
|
|||
|
if (!~rt) { return 0; }
|
|||
|
dep %= DIM;
|
|||
|
int ret = 0, cur;
|
|||
|
for (cur = 0; cur < DIM; ++cur) {
|
|||
|
if (kdt[rt].x[cur] < pt1[cur] || kdt[rt].x[cur] > pt2[cur]) { break; }
|
|||
|
}
|
|||
|
if (cur == DIM) { ++ret; }
|
|||
|
if (pt2[dep] < kdt[rt].x[dep]) {
|
|||
|
ret += query(pt1, pt2, lson);
|
|||
|
} else if (pt1[dep] >= kdt[rt].x[dep]) {
|
|||
|
ret += query(pt1, pt2, rson);
|
|||
|
} else {
|
|||
|
ret += query(pt1, pt2, lson);
|
|||
|
ret += query(pt1, pt2, rson);
|
|||
|
}
|
|||
|
return ret;
|
|||
|
}
|
|||
|
//划分树
|
|||
|
int part[20][N]; //表示每层每个位置的值
|
|||
|
int sorted[N]; //已经排序好的数
|
|||
|
int tol[20][N]; //tol[p][i] 表示第i层从1到i有数分入左边
|
|||
|
void build(int l, int r, int dep) {
|
|||
|
if (l == r) { return; }
|
|||
|
int m = l + r >> 1, cnt = m - l + 1; //表示等于中间值而且被分入左边的个数
|
|||
|
for (int i = l; i <= r; ++i) {
|
|||
|
if (part[dep][i] < sorted[m]) { --cnt; }
|
|||
|
}
|
|||
|
int lpos = l, rpos = m + 1;
|
|||
|
for (int i = l; i <= r; ++i) {
|
|||
|
if (part[dep][i] < sorted[m]) {
|
|||
|
part[dep + 1][lpos++] = part[dep][i];
|
|||
|
} else if (part[dep][i] == sorted[m] && cnt > 0) {
|
|||
|
part[dep + 1][lpos++] = part[dep][i];
|
|||
|
--cnt;
|
|||
|
} else {
|
|||
|
part[dep + 1][rpos++] = part[dep][i];
|
|||
|
}
|
|||
|
tol[dep][i] = tol[dep][l - 1] + lpos - l;
|
|||
|
}
|
|||
|
build(l, m, dep + 1);
|
|||
|
build(m + 1, r, dep + 1);
|
|||
|
}
|
|||
|
//离线查询区间第k大的数, [L, R]是要查询的小区间, [l, r]是大区间
|
|||
|
int query(int L, int R, int k, int l, int r, int dep) {
|
|||
|
if (L == R) { return part[dep][L]; }
|
|||
|
int m = l + r >> 1, cnt = tol[dep][R] - tol[dep][L - 1];
|
|||
|
if (cnt >= k) {
|
|||
|
int tl = l + tol[dep][L - 1] - tol[dep][l - 1], tr = tl + cnt - 1;
|
|||
|
return query(tl, tr, k, l, m, dep + 1);
|
|||
|
} else {
|
|||
|
int tr = R + tol[dep][r] - tol[dep][R], tl = tr - (R - L - cnt);
|
|||
|
return query(tl, tr, k - cnt, m + 1, r, dep + 1);
|
|||
|
}
|
|||
|
}
|
|||
|
//左偏树
|
|||
|
//可并堆的一种实现, 可以在O(logn)的时间内实现堆的push、pop和两个堆的合并操作, 以及O(1)时间的取堆顶操作
|
|||
|
int val[N], ls[N], rs[N], dep[N], fa[N];
|
|||
|
void init(int n) {
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d", &val[i]); ls[i] = rs[i] = dep[i] = 0; fa[i] = i;
|
|||
|
}
|
|||
|
}
|
|||
|
int find(int x) { return x == fa[x] ? x : fa[x] = findfa(fa[x]); }
|
|||
|
int merge(int x, int y) {
|
|||
|
if (!x || !y) { return x | y; }
|
|||
|
if (val[x] < val[y]) { swap(x, y); }
|
|||
|
rs[x] = merge(rs[x], y); fa[rs[x]] = x;
|
|||
|
if (dep[ls[x]] < dep[rs[x]]) { swap(ls[x], rs[x]); }
|
|||
|
dep[x] = dep[rs[x]] + 1;
|
|||
|
return x;
|
|||
|
}
|
|||
|
int push(int x, int y) { return merge(x, y); }
|
|||
|
int pop(int x) {
|
|||
|
int a = ls[x], b = rs[x];
|
|||
|
ls[x] = rs[x] = dep[x] = 0;
|
|||
|
fa[x] = x; fa[a] = a; fa[b] = b;
|
|||
|
return merge(a, b);
|
|||
|
}
|
|||
|
//POJ 2201
|
|||
|
int main() {
|
|||
|
int n, m, x, y;
|
|||
|
while (~scanf("%d", &n)) {
|
|||
|
init(n);
|
|||
|
scanf("%d", &m);
|
|||
|
while (m--) {
|
|||
|
scanf("%d%d", &x, &y);
|
|||
|
int a = find(x), b = find(y);
|
|||
|
if (a == b) {
|
|||
|
puts("-1");
|
|||
|
} else {
|
|||
|
val[a] >>= 1; val[b] >>= 1;
|
|||
|
a = push(pop(a), a); b = push(pop(b), b);
|
|||
|
printf("%d\n", val[merge(a, b)]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
//笛卡尔树
|
|||
|
//考虑一个键值对的序列, 当键与键, 值与值之间互不相同时, 它们可以唯一地构成这样一棵二叉树:
|
|||
|
//key在中序遍历时呈升序, 满足二叉查找树性质; 父节点的value大于子节点的value, 满足堆的性质.
|
|||
|
//一个键值对序列的笛卡儿树可以O(n)时间内构造出来
|
|||
|
//POJ2201
|
|||
|
const int N = 50005;
|
|||
|
int idx[N], n;
|
|||
|
struct Cartesian_Tree {
|
|||
|
int root, key[N], val[N], ch[N][2], pre[N];
|
|||
|
void init() {
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
scanf("%d%d", &key[i], &val[i]);
|
|||
|
ch[i][0] = ch[i][1] = pre[i] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
void build() {
|
|||
|
static int st[N];
|
|||
|
int top = -1;
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
int k = top;
|
|||
|
while (k >= 0 && val[st[k]] > val[idx[i]]) {
|
|||
|
--k;
|
|||
|
}
|
|||
|
if (~k) {
|
|||
|
pre[idx[i]] = st[k];
|
|||
|
ch[st[k]][1] = idx[i];
|
|||
|
}
|
|||
|
if (k < top) {
|
|||
|
pre[st[k + 1]] = idx[i];
|
|||
|
ch[idx[i]][0] = st[k + 1];
|
|||
|
}
|
|||
|
st[++k] = idx[i];
|
|||
|
top = k;
|
|||
|
}
|
|||
|
root = st[0];
|
|||
|
}
|
|||
|
} ct;
|
|||
|
bool cmp(int x, int y) {
|
|||
|
return ct.key[x] < ct.key[y];
|
|||
|
}
|
|||
|
int main() {
|
|||
|
while (~scanf("%d", &n)) {
|
|||
|
ct.init();
|
|||
|
for (int i = 1; i <= n; ++i) { idx[i] = i; }
|
|||
|
sort(idx + 1, idx + n + 1, cmp);
|
|||
|
ct.build();
|
|||
|
puts("YES");
|
|||
|
for (int i = 1; i <= n; ++i) {
|
|||
|
printf("%d %d %d\n", ct.pre[i], ct.ch[i][0], ct.ch[i][1]);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|