|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
总结与反思
A - 数据结构维护前缀和要提前加一个 0, 不然 1 是取不到的 C - 最小生成树相关问题两种思考: 按照边权分类 或者 建一个树然后树剖加边 D - 思考问题先想降维的情况, 本题的思路就是由 l == r 的情况想出来的
C - 又见最小生成树
现在给出一幅个节点条边的无向连通图, 请判断原图中的每条边是否一定在最小生成树中
对于每条边 :
如果这条边一定出现在任意一棵最小生成树中, 输出 1
如果一定不会出现在最小生成树中, 输出 2
如果无法确定, 输出 0
最小生成树问题我们一般想到按照边权分类, 同边权的一起考虑
这里有一个建图技巧就是 *2 + 1
假设前面的边都加进去, 现在有一些边连接这些连通块
如果是割边, 就一定经过
如果在连通块里面, 由于连通块里面的边都比现在的边小, 所以一定不经过
剩下的是可能的#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
struct Edge {
int u, v, w, id;
} e[N];
int n, m, ans[N];
struct DSU {
int pa[N];
DSU() {
for (int i = 1; i < N; i++) pa[i] = i;
}
int Find(int x) {
if (x == pa[x])
return x;
return (pa[x] = Find(pa[x]));
}
void Merge(int x, int y) { pa[x] = y; }
} dsu;
// 每次循环新建图和加入的点
vector<int> point;
vector<pii> g[N];
int dfn[N], low[N], tot;
void Tarjan(int u, int eid) {
dfn[u] = low[u] = ++tot;
for (auto ed : g[u]) {
int v = ed.first;
if (!dfn[v]) {
Tarjan(v, ed.second);
low[u] = min(low[u], low[v]);
// 如果这个新边是割边, 就必须经过
if (dfn[u] < low[v]) {
ans[ed.second >> 1] = 1;
}
} else if ((eid >> 1) != (ed.second >> 1)) {
low[u] = min(dfn[v], low[u]);
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
freopen("mstagain.in", "r", stdin);
freopen("mstagain.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
e[i] = { u, v, w, i };
}
sort(e + 1, e + m + 1, [](Edge& a, Edge& b) { return a.w < b.w; });
for (int i = 1; i <= m; i++) {
// 找出权值相等的区间
int l = i, r = i;
while (r <= m && e[r].w == e[l].w) r++;
r--;
// Kruskal
for (int j = l; j <= r; j++) {
int u = dsu.Find(e[j].u), v = dsu.Find(e[j].v);
if (u == v) {
// 已经成连通块了, 里面的任何边都小于现在的权值, 一定不取
ans[e[j].id] = 2;
continue;
}
// 如果没成就连边(新图)
point.emplace_back(u);
point.emplace_back(v);
// 编号小技巧轻松判断反边, 更新答案也好
g[u].emplace_back(v, e[j].id << 1);
g[v].emplace_back(u, e[j].id << 1 | 1);
}
for (auto j : point) {
if (!dfn[j])
Tarjan(j, 0);
}
for (int j = l; j <= r; j++) {
int u = dsu.Find(e[j].u), v = dsu.Find(e[j].v);
// 把所有边权一样的边都连起来, 变成新的图
if (u == v)
continue;
dsu.Merge(u, v);
}
// 清零等着复用
for (auto j : point) {
g[j].clear();
dfn[j] = low[j] = 0;
}
point.clear();
tot = 0;
i = r;
}
for (int i = 1; i <= m; i++) {
cout << ans[i] << '\n';
}
return 0;
}
D - 排列
给你 n 个点, 有点权
你需要构造一个排列, 使得最终的满足条件的点的权值之和最大
每个点都有一个前置区间 [l, r] , 表示如果范围内的某个点排在 i 的前面了,那么 i 点的权值就要被累加进去
应该如何排列, 使得点权和才能最大
当 n <= 20 时, 可以状压 dp 拿 30 分
接下来考虑 l == r 的情况 (先考虑一维的问题) , 发现本质上是区间内的点向 i 连边
在一个 scc 中, 每个点都可以互通, 所以权值都可以加, 入度为 0 的 scc 需要牺牲一个最小的权值当作第一个数, 后面的都能加
然后区间的连边需要线段树优化建图#include <bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
const int M = 1e5 + 5;
vector<int> e[N];
int n, ans, val[M];
struct SGT {
#define lc u << 1
#define rc u << 1 | 1
int tot, t[N];
void Build(int u, int l, int r) {
if (l == r) {
t[u] = l;
return;
}
int mid = (l + r) >> 1;
Build(lc, l, mid);
Build(rc, mid + 1, r);
t[u] = ++tot;
e[t[lc]].emplace_back(t[u]);
e[t[rc]].emplace_back(t[u]);
}
void Modify(int u, int l, int r, int ql, int qr, int to) {
if (ql <= l && r <= qr) {
e[t[u]].emplace_back(to);
return;
}
int mid = (l + r) >> 1;
if (ql <= mid)
Modify(lc, l, mid, ql, qr, to);
if (qr > mid)
Modify(rc, mid + 1, r, ql, qr, to);
}
} sgt;
int dfn[N], low[N], tot;
int stk[N], top;
int scc, bel[N], minv[N], din[N];
bitset<N> instk;
void Tarjan(int u) {
dfn[u] = low[u] = ++tot;
stk[++top] = u;
instk[u] = 1;
for (auto v : e[u]) {
if (!dfn[v]) {
Tarjan(v);
low[u] = min(low[u], low[v]);
} else if (instk[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
int v;
scc++;
do {
v = stk[top--];
instk[v] = 0;
bel[v] = scc;
if (v <= n)
minv[scc] = min(minv[scc], val[v]);
} while (v != u);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
freopen("permutation.in", "r", stdin);
freopen("permutation.out", "w", stdout);
cin >> n;
sgt.tot = n;
sgt.Build(1, 1, n);
for (int i = 1; i <= n; i++) {
int l, r;
cin >> l >> r >> val[i];
ans += val[i];
sgt.Modify(1, 1, n, l, r, i);
}
memset(minv, 0x3f, sizeof(minv));
for (int i = 1; i <= sgt.tot; i++) {
if (!dfn[i])
Tarjan(i);
}
// 入度
for (int i = 1; i <= tot; i++) {
for (auto j : e[i]) {
if (bel[i] != bel[j]) {
din[bel[j]]++;
}
}
}
// 前面没有人, 必须牺牲一个最小值
for (int i = 1; i <= scc; i++) {
if (!din[i])
ans -= minv[i];
}
cout << ans;
return 0;
}
|
评分
-
查看全部评分
|