P3527 MET-Meteors 題解

Laijinyi發表於2024-10-07

Solution

單次二分:二分時間,做這個時間前的所有操作,然後線性統計。

注意到可以整體二分,直接整體二分是 \(O(n\log^2 n)\)

考慮掃描序列,用線段樹維護每個時間段內該位置的增加值,差分一下可以實現。

將這棵線段樹持久化一下,一個國家所有位置同時二分即可 \(O(n\log n)\),可惜空間太大。

回到整體二分,考慮 Solve 相當於一堆區間加後單點查,差分後可線性求出每個國家的權值。

我們要讓 Solve 複雜度與當前詢問個數、修改個數相關,怎麼辦呢?

離散化一下再繼續遞迴即可,這裡可以直接用桶維護。時間 \(O(n\log n)\),空間 \(O(n)\)

Code 1

普通整體二分

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
typedef long long ll;
const int N = 3e5 + 10;
int n, m, k, ans[N];
vector<int> Pos[N];
ll p[N];
struct Operations {
	int l, r, s, tim;
} Opers[N << 1];
int optot, queries[N], tmp[N];

ll sum[N];
void Upd(int x, ll v) {
	for (; x <= n; x += x & -x) sum[x] += v;
}
ll Qry(int x) {
	ll res = 0;
	for (; x; x -= x & -x) res += sum[x];
	return res;
}

void Solve(int l, int r, int Opl, int Opr, int Ql, int Qr) {
	if (Opl > Opr || Ql > Qr) return;
	if (l == r) {
		rep(j, Ql, Qr) ans[queries[j]] = l;
		return;
	}
	int mid = (l + r) >> 1, Opmid = Opl, Lc = Ql, Rc = Qr;
	rep(j, Opl, Opr) {
		auto i = Opers[j];
		if (i.tim <= mid) 
			++Opmid, Upd(i.l, i.s), Upd(i.r + 1, -i.s);
	}
	rep(j, Ql, Qr) {
		int i = queries[j];
		ll sum = 0;
		for (int x : Pos[i]) {
			sum += Qry(x);
			if (sum >= p[i]) break;
		}
		if (sum >= p[i]) {
			tmp[Lc++] = i;
		} else {
			p[i] -= sum;
			tmp[Rc--] = i;
		}
	}
	rep(j, Ql, Qr) queries[j] = tmp[j];
	rep(j, Opl, Opmid - 1) {
		auto i = Opers[j];
		Upd(i.l, -i.s), Upd(i.r + 1, i.s);
	}
	Solve(l, mid, Opl, Opmid - 1, Ql, Lc - 1);
	Solve(mid + 1, r, Opmid, Opr, Rc + 1, Qr);
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> m >> n;
	rep(i, 1, n) {
		int x;
		cin >> x, Pos[x].push_back(i);
	}
	rep(i, 1, m) cin >> p[i];
	cin >> k;
	vector<int> vec;
	rep(i, 1, k) {
		int l, r, s;
		cin >> l >> r >> s;
		if (l <= r) {
			Opers[++optot] = (Operations){l, r, s, i};
		} else {
			Opers[++optot] = (Operations){l, n, s, i};
			Opers[++optot] = (Operations){1, r, s, i};
		}
	}
	rep(i, 1, m) queries[i] = i, ans[i] = k + 1;
	Solve(1, k + 1, 1, optot, 1, m);
	rep(i, 1, m) {
		if (ans[i] == k + 1) {
			cout << "NIE\n";
		} else {
			cout << ans[i] << '\n';
		}
	}
	return 0;
}

Code 2

可持久化線段樹

// O(n log n)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 3e5 + 10, M = 2e7 + 10;
vector<pair<int, int>> Upds[N];
vector<int> Pos[N];
int n, m, k, p[N];
#define mid ((l + r) >> 1)
struct Node {
	int lc, rc;
	ll sum;
} f[M];
int tot, rt[N];
void Build(int &u, int l, int r) {
	f[u = ++tot].sum = 0ll;
	if (l == r) return;
	Build(f[u].lc, l, mid), Build(f[u].rc, mid + 1, r);
}
void Upd(int &u, int l, int r, int x, ll v) {
	if (x < l || r < x) return;
	f[++tot] = f[u], u = tot, f[u].sum += v;
	if (l == r) return;
	Upd(f[u].lc, l, mid, x, v), Upd(f[u].rc, mid + 1, r, x, v);
}
int Qry(vector<int> &nodes, int l, int r, ll v) {
	if (l == r) return l;
	ll sum = 0;
	for (int u : nodes) {
		sum += f[f[u].lc].sum;
		if (sum >= v) break;
	}
	if (v <= sum) {
		for (int & u : nodes) u = f[u].lc;
		return Qry(nodes, l, mid, v);
	} else {
		for (int & u : nodes) u = f[u].rc;
		return Qry(nodes, mid + 1, r, v - sum);
	}
}
#undef mid

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> m >> n;
	rep(i, 1, n) {
		int x;
		cin >> x, Pos[x].push_back(i);
	}
	rep(i, 1, m) {
		cin >> p[i];
	}
	cin >> k;
	rep(i, 1, k) {
		int l, r, s;
		cin >> l >> r >> s;
		if (l <= r) {
			Upds[l].push_back(make_pair(i, s));
			Upds[r + 1].push_back(make_pair(i, -s));
		} else {
			Upds[l].push_back(make_pair(i, s));
			Upds[1].push_back(make_pair(i, s));
			Upds[r + 1].push_back(make_pair(i, -s));
		}
	}
	Build(rt[0], 1, k + 1);
	rep(i, 1, n) {
		rt[i] = rt[i - 1];
		for (auto it : Upds[i]) {
			Upd(rt[i], 1, k + 1, it.first, it.second);
		}
	}
	rep(i, 1, m) {
		vector<int> nodes;
		for (int v : Pos[i]) nodes.push_back(rt[v]);
		int ans = Qry(nodes, 1, k + 1, p[i]);
		if (ans == k + 1) {
			cout << "NIE\n";
		} else {
			cout << ans << '\n';
		}
	}
	return 0;
}

Code 3

\(\log\) 整體二分

#include <bits/stdc++.h>
using namespace std;
#define rep(i, j, k) for (int i = (j); i <= (k); ++i)
#define reo(i, j, k) for (int i = (j); i >= (k); --i)
typedef long long ll;
const int N = 3e5 + 10;
vector<int> Pos[N];
int n, m, k, p[N];
struct Oper {
	int l, r, a, t;
} O[N << 1];
int tot, id[N], _id[N], ans[N];
ll buc[N], Res[N];

void Solve(int l, int r, int ql, int qr, int range) {
	if (ql > qr) return;
	if (l == r) {
		rep(i, ql, qr) ans[id[i]] = l;
		return;
	}
	int mid = (l + r) >> 1, L = ql - 1, R;
	rep(i, 1, range) buc[i] = 0;
	rep(i, ql, qr) for (int v : Pos[id[i]]) buc[v] = 1;
	rep(i, 1, range) buc[i] += buc[i - 1];
	rep(i, ql, qr) for (int & v : Pos[id[i]]) v = buc[v];
	rep(i, l, r) O[i].l = buc[O[i].l - 1] + 1, O[i].r = buc[O[i].r];
	range = buc[range];
	rep(i, 1, range) buc[i] = 0;
	rep(i, l, mid) buc[O[i].l] += O[i].a, buc[O[i].r + 1] -= O[i].a;
	rep(i, 1, range) buc[i] += buc[i - 1];
	rep(i, ql, qr) {
		ll res = 0;
		int now = id[i];
		for (int v : Pos[now]) {
			res += buc[v];
			if (res >= p[now]) break;
		}
		Res[i] = res;
		if (res >= p[now]) _id[++L] = now;
	}
	R = L;
	rep(i, ql, qr) {
		int now = id[i];
		if (Res[i] < p[now]) p[now] -= Res[i], _id[++R] = now;
	}
	rep(i, ql, qr) id[i] = _id[i];
	Solve(l, mid, ql, L, range), Solve(mid + 1, r, L + 1, qr, range);
}

int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr);
	cin >> m >> n;
	rep(i, 1, n) {
		int x;
		cin >> x, Pos[x].push_back(i);
	}
	rep(i, 1, m) cin >> p[i];
	cin >> k;
	rep(i, 1, k) {
		int l, r, s;
		cin >> l >> r >> s;
		if (l <= r) {
			O[++tot] = {l, r, s, i};
		} else {
			O[++tot] = {l, n, s, i}, O[++tot] = {1, r, s, i};
		}
	}
	rep(i, 1, m) ans[i] = tot + 1, id[i] = i;
	Solve(1, tot + 1, 1, m, n);
	rep(i, 1, m) {
		if (ans[i] == tot + 1) {
			cout << "NIE\n";
		} else {
			cout << O[ans[i]].t << '\n';
		}
	}
	return 0;
}