去掉不合适的小组(自身包含奇环)的情况下的所有组的两两组合数目减去不合适的组合。并根据可撤销并查集不停的考虑不同的两组之间的组合是否合适,没进行下一组之前便把之前在上一个两组之间加上的边都去掉。
如果两组之间连完边之后没有奇环的话就合适否则总数减一。
用并查集确定二分图是否含有奇环。
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int cant[1000001], sz[1000001], st[1000001], col[1000001], tot = 0, cnt = 0;
int f[1000001];
int find(int x)
{
while(x!=f[x])
{
x=f[x];
}
return x;
//return f[x] == x ? x : f[x] = find(f[x]);
}
void merge(int a, int b)
{
int fa = find(a), fb = find(b);
if(fa==fb)
{
return;
}
else
{
if(sz[fa]>sz[fb])
{
swap(fa,fb);
}
}
f[fa] = fb;
st[++tot] = fa;
sz[fb] += sz[fa];
}
void roll_back(int y)
{
while (tot > y)
{
int now = st[tot--];
sz[f[now]] -= sz[now];
f[now] = now;
}
}
struct node
{
int u, v, x, y;
bool operator<(const node &T) const
{
if (y == T.y)
return x < T.x;
return y < T.y;
}
} p[1000001];
void init(int n)
{
int i;
for (i = 1; i < n + 1; i++)
{
f[i] = i;
sz[i] = 1;
}
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
#endif
int n, m, k, i, j, u, v;
long long int tmp = 0;
scanf("%d %d %d", &n, &m, &k);
init(n+n);
for (i = 1; i < n + 1; i++)
{
scanf("%d", &col[i]);
}
tmp = k;
for (i = 0; i < m; i++)
{
scanf("%d %d", &u, &v);
if (col[u] == col[v])
{
if (!cant[col[u]] && find(u) == find(v))
{
cant[col[u]] = 1;
tmp--;
continue;
}
merge(u, v + n);
merge(v, u + n);
}
else
{
if (col[u] > col[v])
{
swap(u, v);
}
p[++cnt].u = u, p[cnt].v = v, p[cnt].x = col[u], p[cnt].y = col[v];
}
}
int no = tot;
long long int ans = tmp * (tmp - 1) / 2;
sort(p + 1, p + cnt + 1);
for (i = 1,j; i <= cnt; i = j + 1)
{
roll_back(no);
int ok = 0;
j = i;
while (j + 1 <= cnt && p[j].x == p[j + 1].x && p[j].y == p[j + 1].y)
{
j++;
}
if (cant[p[j].x] == 1 || cant[p[j].y] == 1)
{
continue;
}
for (int now = i; now <= j; now++)
{
int fa = find(p[now].u), fb = find(p[now].v);
if (fa == fb)
{
ok = 1;
break;
}
else
{
merge(p[now].u, p[now].v + n);
merge(p[now].v, p[now].u + n);
}
}
ans -= ok;
}
printf("%lld\n", ans);
return 0;
}