|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
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;
- }
复制代码 |
评分
-
查看全部评分
|