CF1444C 题解
前置知识
- 二分图的基础概念,怎么用并查集判定二分图。
- 可撤销并查集,看这。
思路
比较基础的一道题,可以和模板题相媲美。
先再提一下吧,找能否构成二分图的关键性质在于此图是否有奇环。
我们考虑对于两个端点都是同一组内的边,只要选了这一组就必须要选这些边,所以初始的时候把同组内的边都连上,且这样不会影响其他组。
这时,单组内构不成二分图的那些组别一定不会和其它组产生贡献。所以我们只考虑单组内能构成二分图的那些组。
我们把所有两个端点分别处于 $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;
}

浙公网安备 33010602011771号