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

Posted kaka0010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;

以上是关于CF 891C Envy 最小生成树+可撤销并查集的主要内容,如果未能解决你的问题,请参考以下文章

「CF891C」Envy

CF891C Envy

[CF891C] Envy - Kruskal,并查集

CF891C Envy

[题解] [CF891C] Envy

CF576E Painting Edges [线段树分治,可撤销并查集]