题意
给定n个点,m条边,k个集合。每个点都属于自己的集合,每次取任意两个集合构成的子图,问是否是二分图,最后求二分子图的个数。
分析
根据题目的数据范围,暴力枚举子图加dfs判二分图是肯定会超时的,因此考虑另一种判二分图的方法,种类并查集。
种类并查集判二分图的方法:假设x和y分别处于二分图的两部分,那么我们建立两个点的虚点x+n和y+n代表对立面的点,因此从x和y+n是处于一个集合中的,y和x+n同理。最后如果发现x和y在一个集合中,说明存在奇环即不是二分图。
然后我们考虑两个集合中的点是怎么产生贡献的
- 如果同一个集合中的点就已经产生奇环,那么无论是哪个集合和它相连一定不存在贡献
- 两个集合中的点构成奇环
第一种情况我们可以直接处理,先把相邻的同集合元素相连,如果产生奇环就标记该集合。
然后我们再去给边分类。我们将所有连着相同两个集合的边分为一类,就可以枚举所有的情况了。
最后可以通过未标记的集合个数来推算出总的枚举可能,然后减去不符合的情况就可以得出答案了。
代码
#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 = 1e6 + 10, M = 5e5 + 10, INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int n, m, k, cnt;
int c[N], pd[N];
map<PII, int> mp;
vector<int> vec[N];
struct Edge {
int u, v;
}e[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*2; i++) fa[i] = i, siz[i] = 1;
}
int find(int x) {
return fa[x] == x ? x : find(fa[x]);}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return;
if (siz[fx] > siz[fy]) swap(fx, fy), swap(x, y);
siz[fy] += siz[fx], fa[fx] = fy;
st.push({
fx, fy});
}
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 >> k; dsu.init(); ll ans = 0;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= m; i++) cin >> e[i].u >> e[i].v;
for (int i = 1; i <= m; i++) {
int u = e[i].u, v = e[i].v;
if (c[u] == c[v]) {
int fu = dsu.find(u), fv = dsu.find(v);
if (fu == fv) pd[c[u]] = 1;
else dsu.merge(u, v+n), dsu.merge(v, u+n);
}
}
for (int i = 1; i <= m; i++) {
int u = e[i].u, v = e[i].v;
if (c[u] == c[v] || pd[c[u]] || pd[c[v]]) continue;
if (c[u] > c[v]) swap(u, v);
if (!mp[{
c[u], c[v]}]) mp[{
c[u], c[v]}] = ++cnt;
vec[mp[{
c[u], c[v]}]].pb(i);
}
for (auto item : mp) {
int id = item.se;
int now = dsu.st.size(), f = 0;
for (auto it : vec[id]) {
int u = e[it].u, v = e[it].v;
int fu = dsu.find(u), fv = dsu.find(v);
if (fu == fv) {
f = 1; break;
} else {
dsu.merge(u, v+n);
dsu.merge(v, u+n);
}
}
if (f) ans--;
while (dsu.st.size() > now) dsu.undo();
}
int left = 0;
for (int i = 1; i <= k; i++) if (!pd[i]) left++;
ans += 1ll*(left-1)*left/2;
printf("%lld\n", ans);
}
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;
}