【题目链接】
【思路要点】
- 倍长序列,将环上问题化为序列问题。
- 根据Hall定理,如果我们能找到一系列区间使得它们的权值之和大于这些区间并的长度,那么答案为No,否则答案为Yes。
- 显然,找到多于一段的区间是没有意义的,因此,我们认为找到的这一系列区间的并也是一个连续的区间。我们希望找到的区间完全包含的区间的权值之和与其长度的差尽可能大。
- 离散化坐标,枚举最终区间的左端点,对右端点对上述差值的贡献建立线段树,支持区间加减和区间查询最大值。在一个区间脱离当前扫描区域后,将其权值在对应的所有右端点处减去即可。
- 注意特判所有区间的并是整个点集的情况。
- 时间复杂度\(O(TNLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 400005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct info {int r, v; }; vector <info> a[MAXN]; int n, m, tot, tmp[MAXN]; int l[MAXN], r[MAXN], x[MAXN]; long long rsum[MAXN]; struct SegmentTree { struct Node { int lc, rc; long long tag, Max; } a[MAXN * 2]; int root, size, n; void build(int &root, int l, int r) { root = ++size; a[root].Max = a[root].tag = 0; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x) { root = size = 0; n = x; build(root, 1, n); } void update(int root) { a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max); } void pushdown(int root) { long long tmp = a[root].tag; a[root].tag = 0; a[a[root].lc].tag += tmp; a[a[root].lc].Max += tmp; a[a[root].rc].tag += tmp; a[a[root].rc].Max += tmp; } void set(int root, int l, int r, int pos, long long v) { if (l == r) { a[root].Max = v; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= pos) set(a[root].lc, l, mid, pos, v); else set(a[root].rc, mid + 1, r, pos, v); update(root); } void set(int pos, long long v) { set(root, 1, n, pos, v); } void add(int root, int l, int r, int ql, int qr, long long v) { if (l == ql && r == qr) { a[root].Max += v; a[root].tag += v; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) add(a[root].lc, l, mid, ql, min(qr, mid), v); if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, v); update(root); } void add(int l, int r, long long v) { add(root, 1, n, l, r, v); } long long query(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Max; pushdown(root); int mid = (l + r) / 2; long long ans = -1e18; if (mid >= ql) ans = max(ans, query(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) ans = max(ans, query(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } long long query(int l, int r) { return query(root, 1, n, l, r); } } ST; int main() { int T; read(T); while (T--) { read(n), read(m); tot = 0; for (int i = 1; i <= n; i++) { read(l[i]), read(r[i]), read(x[i]); tmp[++tot] = l[i], tmp[++tot] = r[i]; } sort(tmp + 1, tmp + tot + 1); tot = unique(tmp + 1, tmp + tot + 1) - tmp - 1; for (int i = 1; i <= n; i++) { l[i] = lower_bound(tmp + 1, tmp + tot + 1, l[i]) - tmp; r[i] = lower_bound(tmp + 1, tmp + tot + 1, r[i]) - tmp; } for (int i = 1; i <= tot; i++) tmp[i + tot] = tmp[i] + m; for (int i = 1; i <= tot * 2; i++) a[i].clear(); memset(rsum, 0, sizeof(rsum)); ST.init(tot * 2); long long s = 0, t = 0, ans = 0; for (int i = 1; i <= n; i++) { t += x[i]; if (l[i] <= r[i]) { a[l[i] + 1].push_back((info) {r[i], x[i]}); rsum[r[i] - 1] += x[i]; a[l[i] + tot + 1].push_back((info) {r[i] + tot, x[i]}); rsum[r[i] + tot - 1] += x[i]; s += x[i] * 2; } else { a[l[i] + 1].push_back((info) {r[i] + tot, x[i]}); rsum[r[i] + tot - 1] += x[i]; s += x[i]; } } for (int i = tot * 2; i >= 1; i--) { rsum[i] += rsum[i + 1]; ST.set(i, -tmp[i] - rsum[i]); } for (int i = 1; i <= tot; i++) { for (unsigned j = 0; j < a[i].size(); j++) ST.add(a[i][j].r, tot * 2, -a[i][j].v); ans = max(ans, s + ST.query(i, i + tot - 1) + tmp[i] - 1); } if (ans == 0 && t <= m) printf("Yes\n"); else printf("No\n"); } return 0; }