排列 题解

题意简述

给你 \(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;
}
posted @ 2024-09-05 07:52  XuYueming  阅读(4)  评论(0编辑  收藏  举报