排列 題解

XuYueming發表於2024-09-05

題意簡述

給你 \(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;
}

相關文章