CF 891C Envy 最小生成树+可撤销并查集

原题链接

文章目录

题意

给定n个定,m条边,接下来有k组询问,每次给定一个集合,问能否用这些集合的点构成MST。

分析

如果每次只问一条边,我们可以用树剖或树上倍增找到这条链上最大值再替换成询问的边,但现在询问为一个集合,因此问题复杂很多。

关于kruskal有两个很有趣的定理

  1. 定义wi为每一条边,对于任意wi,我们选择长度<wi的边构成的联通块是相同的
  2. 定义最小生成树中边长为wi的边条数有ci条,对于所有的最小生成树来说,wi和ci都是相同的

考虑离线的处理方法,我们把所有询问的边按照权值进行分类,这样就可以做到在处理长度为wi的边时,长度<wi的边都已经处理完成了,如果我们当时加入权值为wi的边时发现构成环,说明这条边是不必要的,因此可以直接标记这个询问为false。但我们发现,相同权值的边处在不同的询问当中,因此我们在处理完一个询问的时候要撤销之前的影响,用可撤销并查集就可以解决。

AC Code

#include <bits/stdc++.h>
//#define ACM_LOCAL
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 5e5 + 10, M = 5e5 + 10, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m, k, cnt;
struct Edge {
    
    
    int u, v, w;
    bool operator < (const Edge &rhs) const {
    
    
        return w < rhs.w;
    }
}e[N];

struct Query {
    
    
    int u, v, id;
    bool operator < (const Query &rhs) const {
    
    
        return id < rhs.id;
    }
};
bool ans[N];
vector<Query> vec[N];
struct Undo_dsu {
    
    
    stack<PII> st;
    int fa[N], siz[N];
    void init() {
    
    
        while (st.size()) st.pop();
        for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
    }
    int find(int x) {
    
    return fa[x] == x ? x : find(fa[x]);}
    bool merge(int x, int y) {
    
    
        int fx = find(x), fy = find(y);
        if (fx == fy) return false;
        if (siz[fx] > siz[fy]) swap(fx, fy), swap(x, y);
        siz[fy] += siz[fx], fa[fx] = fy;
        st.push({
    
    fx, fy});
        return true;
    }
    void undo() {
    
    
        PII now = st.top();
        fa[now.fi] = now.fi;
        siz[now.se] -= siz[now.fi];
        st.pop();
    }
}dsu;

void solve() {
    
    
    cin >> n >> m; dsu.init();
    for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v >> e[i].w;
    cin >> k; for (int i = 1; i <= k; i++) {
    
    
        int num; cin >> num;
        while (num--) {
    
    
            int x; cin >> x;
            vec[e[x].w].push_back({
    
    e[x].u, e[x].v, i});
        }
    }
    for (int i = 1; i <= N-10; i++) if(vec[i].size()) sort(vec[i].begin(), vec[i].end());
    for (int i = 1; i <= k; i++) ans[i] = true;
    sort(e + 1, e + m + 1);
    for (int i = 1; i <= m; ) {
    
    
        int val = e[i].w, now = dsu.st.size();
        for (int j = 0; j < vec[val].size(); j++) {
    
    
            if (!ans[vec[val][j].id]) continue;
            if (j && vec[val][j].id != vec[val][j-1].id) {
    
    
                while (dsu.st.size() > now) dsu.undo();
            }
            if (!dsu.merge(vec[val][j].u, vec[val][j].v)) ans[vec[val][j].id] = false;
        }
        while (e[i].w == val) dsu.merge(e[i].u, e[i].v), i++;
    }
    for (int i = 1; i <= k; i++) printf("%s\n", ans[i] ? "YES" : "NO");
}

int main() {
    
    
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/kaka03200/article/details/111358139