题意: 一维数轴上有 n n n 个雷。第 i i i 个雷在位置 p i p_i pi。 花费 c i c_i ci 的代价可以引爆第 i i i 个雷,并将区间 [ p i − r i , p i + r i ] [p_i-r_i,p_i+r_i] [pi−ri,pi+ri] 的范围的雷全部引爆,引起连锁反应而不需要额外的代价。现在又 q q q 次修改,每次修改一个雷的花费,然后询问使得所有雷爆炸的最小花费是多少。
题解: 建一棵线段树,每个父亲节点向左右儿子分别连一条有向边。然后枚举每一个雷,从 p i p_i pi 所在的叶子节点,向 [ p i − r i , p i + r i ] [p_i-r_i,p_i+r_i] [pi−ri,pi+ri] 对应的节点连边,tarjan 求一次 scc。删掉所有可以从含有叶子节点的 scc 到达的 scc,剩下的每个包含叶子的 scc 的最小值之和就是答案。修改可以每个 scc 维护一个 multiset。时间复杂度 O ( n log n ) \mathcal{O}(n\log{n}) O(nlogn) 。
代码:
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int maxm = maxn << 2;
vector g[maxm], h[maxm];
int dfn[maxm], low[maxm], belong[maxm], scc, cnt;
bool vis[maxm];
stack s;
void tarjan(int u) {
low[u] = dfn[u] = ++cnt;
s.push(u);
vis[u] = 1;
for (int v: g[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (vis[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
++scc;
int v;
do {
v = s.top();
s.pop();
vis[v] = 0;
belong[v] = scc;
} while (u != v);
}
}
int id[maxn];
struct SegmentTree {
#define ls (rt<<1)
#define rs (rt<<1|1)
void build(int l, int r, int rt) {
if (l == r) {
id[l] = rt;
return;
}
int m = (l + r) / 2;
g[rt].push_back(ls);
g[rt].push_back(rs);
build(l, m, ls);
build(m+1, r, rs);
}
void query(int l, int r, int p, int L, int R, int rt) {
if (l <= L && R <= r) {
g[p].push_back(rt);
return;
}
int m = (L + R) / 2;
if (l <= m) query(l, r, p, L, m, ls);
if (r > m) query(l, r, p, m+1, R, rs);
}
} T;
struct node {
int p, r, c;
friend bool operator< (const node& x, const node& y) {
return x.p < y.p;
}
} a[maxn];
int rk[maxn], in[maxm];
vector> dx;
multiset ms[maxm];
bool ins[maxm];
int main() {
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &a[i].p, &a[i].r, &a[i].c);
dx.push_back({a[i].p, i});
}
sort(dx.begin(), dx.end());
T.build(1, n, 1);
for (int i = 1; i <= n; i++) {
rk[i] = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p, i)) - dx.begin() + 1;
int l = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p - a[i].r, 0)) - dx.begin() + 1;
int r = lower_bound(dx.begin(), dx.end(), make_pair(a[i].p + a[i].r + 1, 0)) - dx.begin();
T.query(l, r, id[rk[i]], 1, n, 1);
}
int m = 4 * n;
for (int i = 1; i <= m; i++) {
if (!dfn[i]) tarjan(i);
}
for (int i = 1; i <= scc; i++) vis[i] = 0;
for (int i = 1; i <= n; i++) {
vis[belong[id[rk[i]]]] = 1;
ms[belong[id[rk[i]]]].insert(a[i].c);
}
queue Q;
for (int u = 1; u <= m; u++) {
for (int v: g[u]) {
if (belong[u] != belong[v]) {
h[belong[u]].push_back(belong[v]);
if (vis[belong[u]]) {
++in[belong[v]];
if (!vis[belong[v]] && !ins[belong[v]]) {
Q.push(belong[v]);
ins[v] = 1;
}
}
}
}
}
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int v: h[u]) {
in[v] += in[u];
if (!ins[v]) {
Q.push(v);
ins[v] = 1;
}
}
ins[u] = 0;
}
ll sum = 0;
for (int i = 1; i <= scc; i++) {
if (vis[i] && in[i] == 0) sum += *ms[i].begin();
}
while (q--) {
int x, y;
scanf("%d%d", &x, &y);
if (!in[belong[id[rk[x]]]]) {
sum -= *ms[belong[id[rk[x]]]].begin();
auto it = ms[belong[id[rk[x]]]].find(a[x].c);
ms[belong[id[rk[x]]]].erase(it);
ms[belong[id[rk[x]]]].insert(y);
sum += *ms[belong[id[rk[x]]]].begin();
a[x].c = y;
}
printf("%lld\n", sum);
}
return 0;
}