2016 ACM/ICPC亚洲区青岛站
A | B | C | D | E | F | G | H | I | J | K | L | M |
O | O | O | O | $\varnothing$ | $\varnothing$ | $\varnothing$ | $\varnothing$ | $\varnothing$ |
签到

#include <bits/stdc++.h> int n; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); int ans = 0; for (int i = 0; i < n; i++) { int a, b; scanf("%d%d", &a, &b); ans += a * b; } printf("%d\n", ans); } return 0; }
判断所有情况,写丑了。

#include <bits/stdc++.h> const int N = 30; int a[N], b[N]; const int n = 24; bool check() { for (int i = 1; i <= 6; i++) { int j = (i - 1) * 4 + 1; int num = b[j]; for (int cnt = 0; cnt < 4; j++, cnt++) if (b[j] != num) return false; } return true; } int main() { //freopen("in.txt", "r", stdin); int T; scanf("%d", &T); while (T--) { for (int i = 1; i <= 24; i++) scanf("%d", a + i); memcpy(b, a, sizeof(a)); if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(a)); b[5] = a[1]; b[7] = a[3]; b[9] = a[5]; b[11] = a[7]; b[13] = a[9]; b[15] = a[11]; b[3] = a[15]; b[1] = a[13]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[1] = a[5]; b[3] = a[7]; b[5] = a[9]; b[7] = a[11]; b[9] = a[13]; b[11] = a[15]; b[15] = a[3]; b[13] = a[1]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[6] = a[2]; b[8] = a[4]; b[10] = a[6]; b[12] = a[8]; b[14] = a[10]; b[16] = a[12]; b[4] = a[16]; b[2] = a[14]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[4] = a[8]; b[2] = a[6]; b[6] = a[10]; b[8] = a[12]; b[10] = a[14]; b[12] = a[16]; b[14] = a[2]; b[16] = a[4]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[3] = a[19]; b[4] = a[20]; b[23] = a[3]; b[24] = a[4]; b[9] = a[24]; b[10] = a[23]; b[20] = a[9]; b[19] = a[10]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[3] = a[23]; b[4] = a[24]; b[23] = a[10]; b[24] = a[9]; b[10] = a[19]; b[9] = a[20]; b[19] = a[3]; b[20] = a[4]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[1] = a[21]; b[2] = a[22]; b[21] = a[12]; b[22] = a[11]; b[12] = a[17]; b[11] = a[18]; b[17] = a[1]; b[18] = a[2]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[1] = a[17]; b[2] = a[18]; b[21] = a[1]; b[22] = a[2]; b[11] = a[22]; b[12] = a[21]; b[18] = a[11]; b[17] = a[12]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[5] = a[23]; b[6] = a[21]; b[23] = a[16]; b[21] = a[15]; b[15] = a[20]; b[16] = a[18]; b[20] = a[6]; b[18] = a[5]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[5] = a[18]; b[6] = a[20]; b[23] = a[5]; b[21] = a[6]; b[16] = a[23]; b[15] = a[21]; b[20] = a[15]; b[18] = a[16]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[7] = a[17]; b[8] = a[19]; b[24] = a[7]; b[22] = a[8]; b[13] = a[22]; b[14] = a[24]; b[19] = a[13]; b[17] = a[14]; if (check()) { puts("YES"); continue; } memcpy(b, a, sizeof(b)); b[7] = a[24]; b[8] = a[22]; b[24] = a[14]; b[22] = a[13]; b[13] = a[19]; b[14] = a[17]; b[19] = a[8]; b[17] = a[7]; if (check()) { puts("YES"); continue; } puts("NO"); } return 0; }
[C. Pocky]
当 $n > d$ 时,$f(n) = \dfrac{\int_{d} ^{n} f(x)dx}{n} + 1$
当 $n \leq d$ 时,$f(n) = 0$
解得 $f(n) = ln(\dfrac{n}{d}) + 1$

#include<bits/stdc++.h> const double eps = 1e-10; int dcmp(double x) { if (fabs(x) < eps) return 0; return x < 0 ? -1 : 1; } int main() { int T; scanf("%d", &T); while (T--) { double a, b; scanf("%lf%lf", &a, &b); if (dcmp(a - b) <= 0) { puts("0.000000"); continue; } double ans = log(a) - log(b) + 1.0; printf("%.6f\n", ans); } return 0; }
每种硬币都是独立的。那么求出第 $i$ 种硬币在第 $j$ 轮之前都被拿掉的概率 $die[i][j] = (1 - p_{i} ^ j)^{cnt_i}$。$alive[i][j] = 1 - die[i][j]$ 表示第 $i$ 种硬币在第 $j$ 轮还存活的概率。
对于第 $i$ 种硬币的答案就是 $\sum_{step} (alive[i][step] - alive[i][step + 1]) * \prod_{j!=i}die[j][step]$。

#include <bits/stdc++.h> const int N = 15; const int step = 105; double qp(double a, int n) { double ans = 1; while (n) { if (n & 1) ans *= a; a *= a; n >>= 1; } return ans; } double alive[N][step], die[N][step], p[N]; int cnt[N], n; void init(int index) { double pp = p[index]; for (int i = 1; i < step; i++) { die[index][i] = qp(1 - pp, cnt[index]); alive[index][i] = 1 - die[index][i]; pp *= p[index]; } } double solve(int index) { double ans = 0; for (int i = 1; i < step - 1; i++) { double temp = alive[index][i] - alive[index][i + 1]; for (int j = 1; j <= n; j++) { if (j == index) continue; temp *= die[j][i]; } ans += temp; } return ans; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d%lf", cnt + i, p + i); init(i); } if (n == 1) { puts("1.000000"); continue; } for (int i = 1; i <= n; i++) printf("%.6f%c", solve(i), " \n"[i == n]); } return 0; }
求出最小的崩溃概率,即求最大的未崩溃概率。
转化成二分图。人多于食物的为 $X$,人少于食物的为 $Y$。
概率取对数,跑费用流即可。注意判最短路时的精度问题。

#include <bits/stdc++.h> const int N = 500 + 7; const int M = 2e4 + 7; const double inf = 1e12; const int INF = 0x3f3f3f3f; struct E { int v, ne, f; double c; } e[M]; int head[N], cnt; double dis[N]; int path[N], n, m; bool inq[N]; inline void add(int u, int v, int f, double c) { e[cnt].v = v; e[cnt].f = f; e[cnt].c = c; e[cnt].ne = head[u]; head[u] = cnt++; e[cnt].v = u; e[cnt].f = 0; e[cnt].c = -c; e[cnt].ne = head[v]; head[v] = cnt++; } const double eps = 1e-8; int dcmp(double x) { if (fabs(x) < eps) return 0; return x < 0 ? -1 : 1; } bool spfa(int s, int t) { for (int i = 0; i <= t; i++) dis[i] = inf, inq[i] = 0, path[i] = -1; dis[s] = 0; inq[s] = 1; std::queue<int> que; que.push(s); while (!que.empty()) { int u = que.front(); que.pop(); inq[u] = 0; for (int i = head[u]; ~i; i = e[i].ne) { int v = e[i].v; double c = e[i].c; if (e[i].f && dcmp(dis[v] - dis[u] - c) > 0) { dis[v] = dis[u] + c; path[v] = i; if (!inq[v]) { inq[v] = 1; que.push(v); } } } } return dis[t] != inf; } double mcf(int s, int t) { double ans = 0; while (spfa(s, t)) { int x = INF; for (int i = path[t]; ~i; i = path[e[i ^ 1].v]) x = std::min(x, e[i].f); ans += dis[t] * x; for (int i = path[t]; ~i; i = path[e[i ^ 1].v]) e[i].f -= x, e[i ^ 1].f += x; } return ans; } int main() { //freopen("in.txt", "r", stdin); int T; scanf("%d", &T); while (T--) { memset(head, -1, sizeof(head)); cnt = 0; scanf("%d%d", &n, &m); int s = 0, t = n + 1; for (int i = 1; i <= n; i++) { int S, B; scanf("%d%d", &S ,&B); if (S > B) add(s, i, S - B, 0); else if(S<B)add(i, t, B - S, 0); } for (int i = 1; i <= m; i++) { int u, v, c; double p; scanf("%d%d%d%lf", &u, &v, &c, &p); add(u, v, 1, 0); if (c > 1) add(u, v, c - 1, -log(1.0 - p)); } printf("%.2f\n", 1.0 - exp(-mcf(s, t))); } return 0; }
图是若干团的并的充要条件是不存在一个三元组其中只有两条边。维护只有两条边的三元组,每一步枚举要删去哪条边或者加上哪条边,当没有这样的三元组时为一个合法的方案。搜十步即可。
删去一条边或加上一条边最多影响 $n$ 个三元组,暴力修改即可。
维护三元组可以用像前向星的方法,加直接在末端添加,删把末端的加到要删的位置上即可。
复杂度 $O(n3^{10})$

#include <cstdio> #include <algorithm> #include <cstring> const int N = 107; int mp[N][N], n; struct SET { int tol, edge[N * N * N], head[N * N * N]; void insert(int x) { edge[++tol] = x; head[x] = tol; } void erase(int x) { head[edge[tol]] = head[x]; edge[head[x]] = edge[tol]; tol--; } bool empty() { return tol == 0; } void clear() { tol = 0; } } st; int ans; inline int cal(int i, int j, int k) { int a[3] = {i, j, k}; std::sort(a, a + 3); return (a[0] - 1) * n * n + (a[1] - 1) * n + (a[2] - 1); } inline void add(int i, int j, int k) { if (mp[i][j] + mp[j][k] + mp[k][i] == 2) st.insert(cal(i, j, k)); } inline void del(int i, int j, int k) { if (mp[i][j] + mp[j][k] + mp[k][i] == 2) st.erase(cal(i, j, k)); } void solve(int i, int j) { for (int k = 1; k <= n; k++) if (k != i && k != j) del(i, j, k); mp[i][j] ^= 1, mp[j][i] ^= 1; for (int k = 1; k <= n; k++) if (k != i && k != j) add(i, j, k); } void dfs(int dep) { if (st.empty()) ans = std::min(ans, dep - 1); if (dep >= ans) return; int t = st.edge[1], c = t % n + 1, b = t / n % n + 1, a = t / n / n + 1; solve(a, b); dfs(dep + 1); solve(a, b); solve(b, c); dfs(dep + 1); solve(b, c); solve(a, c); dfs(dep + 1); solve(a, c); } int main() { int T; scanf("%d", &T); for (int kase = 1; kase <= T; kase++) { scanf("%d", &n); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf("%d", &mp[i][j]); st.clear(); for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) for (int k = j + 1; k <= n; k++) add(i, j, k); ans = 11; dfs(1); printf("Case #%d: %d\n", kase, ans == 11 ? -1 : ans); } return 0; }
KD-tree查最近点对。用最小花费和估价函数剪枝。

#include <bits/stdc++.h> #define ll long long #define pii pair<ll, int> #define fi first #define se second namespace IO { char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = ' '; int p, p3 = -1; void read() {} void print() {} inline int getc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; } inline void flush() { fwrite(buf2, 1, p3 + 1, stdout), p3 = -1; } template <typename T, typename... T2> inline void read(T &x, T2 &... oth) { T f = 1; x = 0; char ch = getc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); } x *= f; read(oth...); } template <typename T, typename... T2> inline void print(T x, T2... oth) { if (p3 > 1 << 20) flush(); if (x < 0) buf2[++p3] = 45, x = -x; do { a[++p] = x % 10 + 48; } while (x /= 10); do { buf2[++p3] = a[p]; } while (--p); buf2[++p3] = hh; print(oth...); } } // using namespace IO #define read IO::read #define print IO::print #define flush IO::flush template<class T> inline void checkmax(T &a, T b) { if (a < b) a = b; } template<class T> inline void checkmin(T &a, T b) { if (a > b) a = b; } ll sqr(int x) { return 1LL * x * x; } const int N = 2e5 + 7; const ll inf = 0x3f3f3f3f3f3f3f3f; const int INF = 0x3f3f3f3f; int n, m, D, root; std::pii ans; struct Node { int lp, rp, id; int d[3], mx[3], mn[3]; inline bool operator < (const Node &rhs) const { return d[D] < rhs.d[D]; } void clear() { lp = rp = id = 0; for (int i = 0; i < 3; i++) d[i] = mx[i] = mn[i] = 0; } } tree[N], qu, in[N]; struct Kd { #define lp tree[p].lp #define rp tree[p].rp inline void pushup(int p, int s) { for (int i = 0; i < 3; i++) checkmax(tree[p].mx[i], tree[s].mx[i]), checkmin(tree[p].mn[i], tree[s].mn[i]); } inline void pushup(int p) { for (int i = 0; i < 3; i++) tree[p].mx[i] = tree[p].mn[i] = tree[p].d[i]; if (lp) pushup(p, lp); if (rp) pushup(p, rp); } int build(int l, int r, int d) { D = d; int mid = l + r >> 1, p = mid; std::nth_element(tree + l, tree + mid, tree + r + 1); if (l != mid) lp = build(l, mid - 1, (d + 1) % 3); else lp = 0; if (r != mid) rp = build(mid + 1, r, (d + 1) % 3); else rp = 0; pushup(p); return p; } ll gu(int p) { if (qu.d[2] < tree[p].mn[2]) return inf + 1; ll ret = 0; for (int i = 0; i < 2; i++) { if (qu.d[i] > tree[p].mx[i]) ret += sqr(qu.d[i] - tree[p].mx[i]); if (qu.d[i] < tree[p].mn[i]) ret += sqr(qu.d[i] - tree[p].mn[i]); } return ret; } ll dis(int p) { if (qu.d[2] < tree[p].d[2]) return inf + 1; ll ret = 0; for (int i = 0; i < 2; i++) ret += sqr(qu.d[i] - tree[p].d[i]); return ret; } void query(int p) { if (!p) return; ll cur = dis(p); if (cur < ans.fi || (cur == ans.fi && tree[p].id < ans.se)) ans.fi = cur, ans.se = tree[p].id; ll dl = 0, dr = 0; if (lp) dl = gu(lp); if (rp) dr = gu(rp); if (dl < dr) { if (dl <= ans.fi && lp) query(lp); if (dr <= ans.fi && rp) query(rp); } else { if (dr <= ans.fi && rp) query(rp); if (dl <= ans.fi && lp) query(lp); } } } kd; int main() { freopen("in.txt", "r", stdin); int T; read(T); for ( ; T--; ) { read(n, m); for (int i = 1; i <= n; i++) { tree[i].clear(); tree[i].id = i; for (int j = 0; j < 3; j++) read(tree[i].d[j]); in[i] = tree[i]; } root = kd.build(1, n, 1); for (int i = 1; i <= m; i++) { ans.fi = inf, ans.se = 0; for (int j = 0; j < 3; j++) read(qu.d[j]); kd.query(root); printf("%d %d %d\n", in[ans.se].d[0], in[ans.se].d[1], in[ans.se].d[2]); } } return 0; }
用ST表+欧拉序维护LCA,$O(1)$ 得到LCA,再用ST表维护dfs序的区间直径,$O(1)$ 得到合并后的区间直径。(第二部分用了线段树狂T)
删去两条边后,最多产生 $5$ 个dfs序区间,考虑几种合并情况即可。

#include <cstdio> #include <cstring> #include <algorithm> #include <cctype> #include <vector> inline void checkmax(int &a, int b) { if (a < b) a = b; } inline void checkmin(int &a, int b) { if (a > b) a = b; } namespace IO { char buf[1 << 21], buf2[1 << 21], a[20], *p1 = buf, *p2 = buf, hh = '\n'; int p, p3 = -1; void read() {} void print() {} inline int getc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++; } inline void flush() { fwrite(buf2, 1, p3 + 1, stdout), p3 = -1; } template <typename T, typename... T2> inline void read(T &x, T2 &... oth) { T f = 1; x = 0; char ch = getc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getc(); } x *= f; read(oth...); } template <typename T, typename... T2> inline void print(T x, T2... oth) { if (p3 > 1 << 20) flush(); if (x < 0) buf2[++p3] = 45, x = -x; do { a[++p] = x % 10 + 48; } while (x /= 10); do { buf2[++p3] = a[p]; } while (--p); buf2[++p3] = hh; print(oth...); } } // using namespace IO #define read IO::read #define print IO::print #define flush IO::flush #define pii pair<int, int> #define fi first #define se second #define pb push_back const int N = 1e5 + 7; const int INF = 0x3f3f3f3f; int n, q, dep[N], dis[N], dfn[N], in[N], out[N], tol, pos[N], euler[2 * N]; int st[2 * N][20], lg[2 * N]; struct E { int v, ne, w; } e[N << 1]; int head[N], cnt; inline void add(int u, int v, int w) { e[++cnt].v = v; e[cnt].ne = head[u]; e[cnt].w = w; head[u] = cnt; } struct Edge { int u, v, w; } edge[N]; void dfs1(int u, int fa) { dfn[in[u] = ++tol] = u; for (int i = head[u]; i; i = e[i].ne) { int v = e[i].v, w = e[i].w;; if (v == fa) continue; dis[v] = dis[u] + w; dep[v] = dep[u] + 1; dfs1(v, u); } out[u] = tol; } void dfs2(int u, int fa) { euler[pos[u] = ++tol] = u; for (int i = head[u]; i; i = e[i].ne) { int v = e[i].v, w = e[i].w;; if (v == fa) continue; dfs2(v, u); euler[++tol] = u; } } inline int min(int a, int b) { return dep[euler[a]] < dep[euler[b]] ? a : b; } void init() { for (int i = 1; i <= 2 * n - 1; i++) st[i][0] = i; for (int j = 1; (1 << j) <= 2 * n - 1; j++) for (int i = 1; i + (1 << j) - 1 <= 2 * n - 1; i++) st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } inline int Lca(int u, int v) { u = pos[u], v = pos[v]; if (u > v) std::swap(u, v); int logg = lg[v - u + 1]; int lca = euler[min(st[u][logg], st[v - (1 << logg) + 1][logg])]; return lca; } inline int getdis(int u, int v) { return dis[u] + dis[v] - 2 * dis[Lca(u, v)]; } /*struct Seg { #define lp p << 1 #define rp p << 1 | 1 int tree[N << 2], p1[N << 2], p2[N << 2]; void pushup(int p) { checkmax(tree[p], tree[lp]); checkmax(tree[p], tree[rp]); int lu = p1[lp], lv = p2[lp], ru = p1[rp], rv = p2[rp]; checkmax(tree[p], getdis(lu, ru)); checkmax(tree[p], getdis(lu, rv)); checkmax(tree[p], getdis(lv, ru)); checkmax(tree[p], getdis(lv, rv)); if (tree[p] == tree[lp]) p1[p] = p1[lp], p2[p] = p2[lp]; else if (tree[p] == tree[rp]) p1[p] = p1[rp], p2[p] = p2[rp]; else if (tree[p] == getdis(lu, ru)) p1[p] = lu, p2[p] = ru; else if (tree[p] == getdis(lu, rv)) p1[p] = lu, p2[p] = rv; else if (tree[p] == getdis(lv, ru)) p1[p] = lv, p2[p] = ru; else p1[p] = lv, p2[p] = rv; } void build(int p, int l, int r) { tree[p] = 0; if (l == r) { p1[p] = p2[p] = dfn[l]; return; } int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); pushup(p); } std::pii query(int p, int l, int r, int x, int y) { if (x > y || x > r || y < l) return std::pii(INF, INF); if (x <= l && y >= r) return std::pii(p1[p], p2[p]); int mid = l + r >> 1; if (x > mid) return query(rp, mid + 1, r, x, y); if (y <= mid) return query(lp, l, mid, x, y); auto pp = query(lp, l, mid, x, y), qq = query(rp, mid + 1, r, x, y); int len = 0; checkmax(len, getdis(pp.fi, pp.se)); checkmax(len, getdis(qq.fi, qq.se)); checkmax(len, getdis(pp.fi, qq.fi)); checkmax(len, getdis(pp.fi, qq.se)); checkmax(len, getdis(pp.se, qq.fi)); checkmax(len, getdis(pp.se, qq.se)); if (len == getdis(pp.fi, pp.se)) return pp; if (len == getdis(qq.fi, qq.se)) return qq; if (len == getdis(pp.fi, qq.fi)) return std::pii(pp.fi, qq.fi); if (len == getdis(pp.fi, qq.se)) return std::pii(pp.fi, qq.se); if (len == getdis(pp.se, qq.fi)) return std::pii(pp.se, qq.fi); return std::pii(pp.se, qq.se); } } seg;*/ std::pii merge(const std::pii &x, const std::pii &y) { if (x.fi == INF || x.se == INF) return y; if (y.fi == INF || y.se == INF) return x; int ans = 0; checkmax(ans, getdis(x.fi, x.se)); checkmax(ans, getdis(y.fi, y.se)); checkmax(ans, getdis(x.fi, y.fi)); checkmax(ans, getdis(x.fi, y.se)); checkmax(ans, getdis(x.se, y.fi)); checkmax(ans, getdis(x.se, y.se)); if (ans == getdis(x.fi, x.se)) return x; if (ans == getdis(y.fi, y.se)) return y; if (ans == getdis(x.fi, y.fi)) return std::pii(x.fi, y.fi); if (ans == getdis(x.fi, y.se)) return std::pii(x.fi, y.se); if (ans == getdis(x.se, y.fi)) return std::pii(x.se, y.fi); return std::pii(x.se, y.se); } struct ST { std::pii St[N][20]; void build() { for (int i = 1; i <= n; i++) St[i][0].fi = St[i][0].se = dfn[i]; for (int j = 1; (1 << j) <= n; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) St[i][j] = merge(St[i][j - 1], St[i + (1 << (j - 1))][j - 1]); } std::pii query(int l, int r) { if (l > r) return std::pii(INF, INF); int logg = lg[r - l + 1]; return merge(St[l][logg], St[r - (1 << logg) + 1][logg]); } } St; int main() { //freopen("in.txt", "r", stdin); int T; read(T); for (int i = 2, j = 0; i < 2 * N; i++) lg[i] = ((1 << (j + 1)) == i) ? ++j : j; for ( ; T-- ; ) { read(n, q); cnt = 0; for (int i = 1; i <= n; i++) { dep[i] = dis[i] = 0; in[i] = out[i] = pos[i] = 0; head[i] = 0; } for (int i = 1, u, v, w; i < n; i++) { read(edge[i].u, edge[i].v, edge[i].w); u = edge[i].u, v = edge[i].v, w = edge[i].w; add(u, v, w); add(v, u, w); } tol = 0; dfs1(1, 0); tol = 0; dfs2(1, 0); init(); for (int i = 1; i < n; i++) if (dep[edge[i].u] > dep[edge[i].v]) std::swap(edge[i].u, edge[i].v); //seg.build(1, 1, n); St.build(); for (int i = 1, u, v, lca; i <= q; i++) { read(u, v); u = edge[u].v, v = edge[v].v, lca = Lca(u, v); int ans = 0; if (lca != u && lca != v) { if (in[u] > in[v]) std::swap(u, v); auto p1 = St.query(1, in[u] - 1), p2 = St.query(out[u] + 1, in[v] - 1), p3 = St.query(out[v] + 1, n); p1 = merge(p1, merge(p2, p3)); checkmax(ans, getdis(p1.fi, p1.se)); p1 = St.query(in[u], out[u]); checkmax(ans, getdis(p1.fi, p1.se)); p1 = St.query(in[v], out[v]); checkmax(ans, getdis(p1.fi, p1.se)); } else { if (u != lca) std::swap(u, v); auto p1 = St.query(1, in[u] - 1), p2 = St.query(out[u] + 1, n); p1 = merge(p1, p2); checkmax(ans, getdis(p1.fi, p1.se)); p1 = St.query(in[u], in[v] - 1), p2 = St.query(out[v] + 1, out[u]); p1 = merge(p1, p2); checkmax(ans, getdis(p1.fi, p1.se)); p1 = St.query(in[v], out[v]); checkmax(ans, getdis(p1.fi, p1.se)); } print(ans); } } flush(); return 0; }
问题转化为 操作 $1$ 加入一个点 $(x, y)$,权值为 $c$。操作 $2$ 给出一个 $pos$,将所有点 $(x, y)$ 满足 $x \leq pos \wedge y \geq pos$ 的权值,并把权值为 $0$ 的点的编号输出并删除。
可以想到用KD-tree维护这么一些点,维护最小权值,以及懒标记维护修改。
删除的地方太过暴力...维护最小权值的节点编号,将这个点的到根的链从上到下把标记pushdown。将其权值修改为INF再从下到上pushup一遍。KDtree期望树高 $O(logn)$,所以可以这么做。

#include <bits/stdc++.h> namespace IO { void read() {} template<class T, class... T2> inline void read(T &x, T2 &... oth) { x = 0; T f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); x *= f; read(oth...); } } using namespace IO; const int N = 4e5 + 7; const int INF = 0x3f3f3f3f; template<class T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; } template<class T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; } int n, m, D, id[N], tol; struct Node { int lp, rp, id, pos, fa; int d[2], mx[2], mn[2]; int v, minv, add; inline bool operator < (const Node &rhs) const { return d[D] < rhs.d[D]; } inline void init() { minv = v = pos = INF, add = 0; for (int i = 0; i < 2; i++) mx[i] = mn[i] = d[i]; } }; struct IN { int x, y, c; } p[N]; namespace KD { #define lp tree[p].lp #define rp tree[p].rp Node tree[N]; int root; inline void pushup(int p, int s) { if (chkmin(tree[p].minv, tree[s].minv)) tree[p].pos = tree[s].pos; for (int i = 0; i < 2; i++) chkmin(tree[p].mn[i], tree[s].mn[i]), chkmax(tree[p].mx[i], tree[s].mx[i]); } inline void pushup(int p) { tree[p].minv = tree[p].v; tree[p].pos = tree[p].id; if (lp) pushup(p, lp); if (rp) pushup(p, rp); } inline void tag(int p, int val) { tree[p].minv += val; tree[p].v += val; tree[p].add += val; } inline void pushdown(int p) { if (!tree[p].add) return; if (lp) tag(lp, tree[p].add); if (rp) tag(rp, tree[p].add); tree[p].add = 0; } void down(int p) { if (tree[p].fa) down(tree[p].fa); pushdown(p); } int build(int l, int r, int d, int fa) { int mid = l + r >> 1; D = d; std::nth_element(tree + l, tree + mid, tree + r + 1); int p = mid; id[tree[p].id] = p; tree[p].fa = fa; tree[p].init(); if (l != mid) lp = build(l, mid - 1, d ^ 1, p); else lp = 0; if (r != mid) rp = build(mid + 1, r, d ^ 1, p); else rp = 0; pushup(p); return p; } void add(int p, int v) { down(p); tree[p].v = v; while (p) { pushup(p); p = tree[p].fa; } } int check(int minx, int maxx, int miny, int maxy, int x) { if (maxx <= x && miny >= x) return 0; if (minx <= x && maxy >= x) return 2; return 1; } void update(int p, int x, int v) { int d = check(tree[p].mn[0], tree[p].mx[0], tree[p].mn[1], tree[p].mx[1], x); if (!d) { tag(p, v); return; } if (d == 1) return; if (tree[p].d[0] <= x && tree[p].d[1] >= x) { tree[p].v += v; tree[p].minv += v; } pushdown(p); if (lp) update(lp, x, v); if (rp) update(rp, x, v); pushup(p); } } using namespace KD; void init() { tol = 0; read(n, m); for (int i = 1; i <= n; i++) { static char s[12]; scanf("%s", s); if (s[0] == 'C') { read(p[i].x, p[i].y, p[i].c); tol++; tree[tol].d[0] = p[i].x; tree[tol].d[1] = p[i].y; tree[tol].id = i; } else { p[i].c = 0; read(p[i].x); } } } void solve() { root = build(1, tol, 0, 0); tree[root].fa = 0; int res = 0; for (int i = 1; i <= n; i++) { if (p[i].c) add(id[i], p[i].c); else update(root, p[i].x ^ res, -1); int val = 0; std::vector<int> temp; while (!tree[root].minv) { val ^= tree[root].pos; temp.push_back(tree[root].pos); add(id[tree[root].pos], INF); } res ^= val; if (!temp.empty()) { printf("%d", i); sort(temp.begin(), temp.end()); for (int x: temp) printf(" %d", x); puts(""); } } } int main() { freopen("in.txt", "r", stdin); int T; read(T); for (int i = 1; i <= T; i++) { printf("Case #%d:\n", i); init(); solve(); } }