CF1444C 题解

前置知识

  1. 二分图的基础概念,怎么用并查集判定二分图。
  2. 可撤销并查集,看

思路

比较基础的一道题,可以和模板题相媲美。

先再提一下吧,找能否构成二分图的关键性质在于此图是否有奇环。

我们考虑对于两个端点都是同一组内的边,只要选了这一组就必须要选这些边,所以初始的时候把同组内的边都连上,且这样不会影响其他组。

这时,单组内构不成二分图的那些组别一定不会和其它组产生贡献。所以我们只考虑单组内能构成二分图的那些组。

我们把所有两个端点分别处于 $i$ 与 $j$ 这两组的边弄到一起去(排序可以实现),然后我们把这些边加到并查集里去,即用并查集去判断是否能构成二分图。而对于新的一对组别——第 $k$ 组以及第 $t$ 组,前面的 $i$ 与 $j$ 这两组的边没用了,所以要撤销掉。而这正是一个可撤销并查集。

#include <bits/stdc++.h>
#define ll long long
#define L(i, a, b) for(int i = a; i <= b; i++)
#define R(i, a, b) for(int i = a; i >= b; i--) 
using namespace std;
const int N = 1e6 + 10;
int n, m, k, c[N], tu[N], sum[N]; ll ans;
vector<pair<int, int> >e[N];
struct DSU{
    int n, top, s[N], fa[N], sz[N];
    void ins(int x){n++, fa[x] = x, sz[x] = 1;}
    int F(int x){return x == fa[x]? x : F(fa[x]);}
    void U(int x, int y){
        x = F(x), y = F(y); if(x == y) return;
        if(sz[x] < sz[y]) swap(x, y);
        fa[y] = x, sz[x] += sz[y], s[++top] = y;
    }
    void D(){
        if(!top) return;
        int y = s[top--]; sz[fa[y]] -= sz[y], fa[y] = y;
    }
    void back(int t){while(top > t) D();}
}d;
bool cmp(pair<int, int> a, pair<int, int> b){return c[a.second] < c[b.second];}
int main(){
    scanf("%d%d%d", &n, &m, &k);
    L(i, 1, n) scanf("%d", &c[i]);
    L(i, 1, n * 2) d.ins(i);
    L(i, 1, k) tu[i] = 1;
    L(i, 1, m){
        int x, y; scanf("%d%d", &x, &y);
        if(c[x] > c[y]) swap(x, y);
        if(c[x] == c[y]){
            if(d.F(x) == d.F(y))
                tu[c[x]] = 0;
            else d.U(x, y + n), d.U(x + n, y);
        }
        else e[c[x]].push_back(make_pair(x, y));
    }
    R(i, k, 1) sum[i] = sum[i + 1] + tu[i];
    L(cl, 1, k){
        if(!tu[cl]) continue;
        sort(e[cl].begin(), e[cl].end(), cmp);
        int flag = 1, t = d.top; ans += sum[cl + 1];
        for(int i = 0; i < e[cl].size(); i++){
            int x = e[cl][i].first, y = e[cl][i].second;
            if(!tu[c[y]]) flag = -1;
            if(flag == 1 && d.F(x) == d.F(y))
                flag = 0;
            else d.U(x, y + n), d.U(x + n, y);
            if(i == e[cl].size() - 1 || c[y] != c[e[cl][i + 1].second]){
                ans -= (!flag), flag = 1, d.back(t);
            }
        }
    }
    printf("%lld\n", ans);
    return 0;
} 
posted @ 2023-02-03 10:48  徐子洋  阅读(19)  评论(0)    收藏  举报  来源