題意簡述
給你 \(n\) 個點,第 \(i\) 個點有點權 \(v_i\)。
你需要構造一個排列,使得最終的滿足條件的點的權值之和最大。
每個點都有一個前置區間 \([l_i, r_i]\),表示如果範圍內的某個點排在 \(i\) 的前面了,那麼 \(i\) 點的權值就要被累加進去。
應該如何排列,使得點權和才能最大。
\(n \leq 10^5\)。
題目分析
直接做並不好做,考慮放到圖論上考慮。讓 \(p \in [l_i, r_i]\) 向 \(i\) 連邊,表示如果走到 \(p\) 就一定可以得到 \(i\) 的貢獻。我們構造排列的過程,就是選取一個點,然後貪心地把這個點能到達的所有點依次加入到排列中,選取的那個點的權值不計,而後來加入的點有貢獻。又由於最終每個點都在排列中,所以我們不妨反向考慮使那些不能產生貢獻的點的權值之和最小。
這個有向圖並沒什麼性質,我們不妨考慮用 tarjan 縮成一個 DAG。發現在 DAG 上,不能產生貢獻的點都在入度為 \(0\) 的分量中,並且每一個這樣的分量必須選取一個點作為用來“啟動”的點。我們不妨在縮點的時候記一個最小值,那麼答案就是所有入度為 \(0\) 的分量的最小值之和。
等一下,如果樸素建邊時間複雜度會爆炸,這也很好解決,使用線段樹最佳化建圖即可。
時間複雜度:\(\Theta(n \log n)\)。
程式碼
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
int n, ans, val[100010];
struct Graph {
struct node {
int to, nxt;
} edge[100010 * 40];
int tot = 1, head[100010 << 2];
void add(int u, int v) {
edge[++tot] = {v, head[u]};
head[u] = tot;
}
inline node & operator [] (const int x) {
return edge[x];
}
} xym;
int whr[100010 << 2], tot;
#define lson (idx << 1 )
#define rson (idx << 1 | 1)
void build(int idx, int l, int r) {
if (l == r) {
whr[idx] = l;
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
whr[idx] = ++tot;
xym.add(whr[lson], whr[idx]);
xym.add(whr[rson], whr[idx]);
}
void add(int idx, int trl, int trr, int l, int r, int u) {
if (trl > r || trr < l) return;
if (l <= trl && trr <= r) return xym.add(whr[idx], u);
int mid = (trl + trr) >> 1;
add(lson, trl, mid, l, r, u);
add(rson, mid + 1, trr, l, r, u);
}
int dfn[100010 << 2], low[100010 << 2], timer;
int stack[100010 << 2], top;
bool ins[100010 << 2];
vector<int> scc[100010 << 2];
int scc_cnt, sccno[100010 << 2];
int mi[100010 << 2];
bool vis[100010 << 2];
void tarjan(int now) {
dfn[now] = low[now] = ++timer;
stack[++top] = now, ins[now] = true;
for (int i = xym.head[now]; i; i = xym[i].nxt) {
int to = xym[i].to;
if (!dfn[to]) tarjan(to), low[now] = min(low[now], low[to]);
else if (ins[to]) low[now] = min(low[now], dfn[to]);
}
if (dfn[now] == low[now]) {
++scc_cnt, mi[scc_cnt] = 0x3f3f3f3f;
do {
int u = stack[top--];
ins[u] = false;
sccno[u] = scc_cnt;
scc[scc_cnt].push_back(u);
if (u <= n)
mi[scc_cnt] = min(mi[scc_cnt], val[u]);
} while (stack[top + 1] != now);
}
}
signed main() {
#ifndef XuYueming
freopen("permutation.in", "r", stdin);
freopen("permutation.out", "w", stdout);
#endif
scanf("%d", &n);
tot = n, build(1, 1, n);
for (int i = 1, L, R; i <= n; ++i) {
scanf("%d%d%d", &L, &R, &val[i]);
ans += val[i];
add(1, 1, n, L, R, i);
}
for (int i = 1; i <= tot; ++i)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= scc_cnt; ++i) {
for (auto u: scc[i]) {
for (int j = xym.head[u]; j; j = xym[j].nxt) {
int v = xym[j].to;
if (sccno[u] == sccno[v]) continue;
vis[sccno[v]] = true;
}
}
}
for (int i = 1; i <= scc_cnt; ++i) {
if (!vis[i] && mi[i] != 0x3f3f3f3f) {
ans -= mi[i];
}
}
printf("%d", ans);
return 0;
}