根据题目性质可知,先不管行之间的连边,任意两行列之间的连边都是相同的,这个性质就很好。
然后有一个可能成立的结论:可以先算出来两行之间列连边中 有连边 的点之间的联通块数量,再算出来三行之间两两相连的列连边中 有连边 的联通块数量。最后用个并查集维护行之间的连边,维护一下行之间的联通块的 siz 和答案。加入一条边时若原本在一个并查集中就用第二种答案,若不在同一个就把两个联通块的答案和第一种答案取 min,就是当前联通块的答案。
最后加上没有连边的答案就行。
但是 WA 了 3 个点,有无大佬能 hack 一下这个想法。
code 如下
#include <bits/stdc++.h>
#define int long long
#define _rep(i, x, y) for(int i = x; i <= y; ++i)
#define _req(i, x, y) for(int i = x; i >= y; --i)
#define _rev(i, u) for(int i = head[u]; i; i = e[i].nxt)
#define pb push_back
#define fi first
#define se second
#define mst(f, i) memset(f, i, sizeof f)
using namespace std;
#ifdef ONLINE_JUDGE
#define debug(...) 0
#else
#define debug(...) fprintf(stderr, __VA_ARGS__), fflush(stderr)
#endif
namespace fastio{
char ibuf[50007],*p1 = ibuf, *p2 = ibuf;
#ifdef ONLINE_JUDGE
#define get() p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, 50007, stdin), p1 == p2) ? EOF : *p1++
#else
#define get() getchar()
#endif
template<typename T> inline void read(T &t){
T x = 0, f = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') f = -f;
c = getchar();
}
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
t = x * f;
}
template<typename T, typename ... Args> inline void read(T &t, Args&... args){
read(t);
read(args...);
}
template<typename T> void write(T t){
if(t < 0) putchar('-'), t = -t;
if(t >= 10) write(t / 10);
putchar(t % 10 + '0');
}
template<typename T, typename ... Args> void write(T t, Args... args){
write(t), putchar(' '), write(args...);
}
template<typename T> void writeln(T t){
write(t);
puts("");
}
template<typename T> void writes(T t){
write(t), putchar(' ');
}
#undef get
};
using namespace fastio;
#define multitest() int T; read(T); _rep(tCase, 1, T)
typedef long long ll;
typedef pair<int, int> PII;
const int N = 3e5 + 5;
int n, m, cnt, cur, cnt2, fa[N], siz[N], res[N];
PII e[N];
bool vis[N], num[N];
int find(int u){return fa[u] == u ? u : fa[u] = find(fa[u]);}
void merge(int u, int v){
int fx = find(u), fy = find(v);
if(fx == fy) return;
if(siz[fx] > siz[fy]) swap(fx, fy);
fa[fx] = fy, siz[fy] += siz[fx], res[fy] = min(res[fy], res[fx]);
}
ll ans;
signed main(){
read(n, m);
_rep(i, 1, 2 * n) siz[fa[i] = i] = 1;
_rep(i, 1, m) read(e[i].fi, e[i].se), merge(e[i].fi, e[i].se + n), merge(e[i].se, e[i].fi + n), num[e[i].fi] = num[e[i].se] = 1;
_rep(i, 1, n) cur += !num[i];
_rep(i, 1, n){
if(vis[find(i)] || !num[i]) continue;
vis[find(i)] = 1, cnt++;
}
_rep(i, 1, 3 * n) vis[fa[i] = i] = 0, siz[i] = 1;
_rep(i, 1, m){
merge(e[i].fi, e[i].se + n), merge(e[i].se, e[i].fi + n);
merge(e[i].fi, e[i].se + n + n), merge(e[i].se, e[i].fi + n + n);
merge(e[i].fi + n, e[i].se + n + n), merge(e[i].se + n, e[i].fi + n + n);
}
_rep(i, 1, n){
if(vis[find(i)] || !num[i]) continue;
vis[find(i)] = 1, cnt2++;
}
// writeln(cnt2);
_rep(i, 1, n) vis[fa[i] = i] = 0, siz[i] = 1, res[i] = cnt;
_rep(i, 1, m){
if(find(e[i].fi) == find(e[i].se)){
res[find(e[i].fi)] = cnt2;
continue;
}
merge(e[i].fi, e[i].se);
}
_rep(i, 1, n){
if(!num[i]){
ans += n;
continue;
}
int x = find(i);
if(vis[x]) continue;
vis[x] = 1, ans += res[x] + cur * siz[x];
}
writeln(ans);
return 0;
}