题目链接
思路:
看完这题第一眼思路是直接枚举两条邻边(m^2),但一看数据范围2e5便知道这个办法不可行,于是考虑将图简化。
简化方法:将原图中度数小的点向度数大的点连一条有向边,如果度数相同便从编号小的向编号大的连边,可以发现这样简化完后的新图是一个DAG(因为如果要成还的话环里的边必然有一条是从度数大的连向度数小的,或者从编号大的连向编号小的,与规则矛盾),而原图中的每个三元环(u,v,w)在新图中一定有一个(u->v,u->w,v->w)的子图与之相对应,我们只需要在新图中枚举每个u的出边,再枚举每个v的出边即可。虽然枚举的时候和我们之前最初的思路差不多,但经过图的简化过后可以证明每个点的出边最多只有根号m条,所以我们在新图上枚举的话复杂度会很神奇的降为O(m√m)即可满足题目要求。
(只能说题解区dalao太强了)
AC代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
#define int long long
const int N = 1e5 + 5, M = 4e5 + 5;
int d[N];
int h[N], e[M], ne[M], idx;
struct node {
int s, t;
};
vector<node> q;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
return;
}
int st[N];
signed main() {
int n, m; cin >> n >> m;
while (m--) {
int a, b; cin >> a >> b;
q.push_back({ a,b });
d[a] += 1;
d[b] += 1;
}
memset(h, -1, sizeof h);
for (int i = 0; i < q.size(); i++) {
int ss = q[i].s, tt = q[i].t;
if (d[ss] < d[tt]) add(ss, tt);
else if (d[ss] > d[tt]) add(tt, ss);
else if (ss < tt) add(ss, tt);
else add(tt, ss);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = h[i]; ~j; j = ne[j]) st[e[j]] = i;
for (int j = h[i]; ~j; j = ne[j]) {
int tt = e[j];
for (int k = h[tt]; ~k; k = ne[k])
if (st[e[k]] == i) ans += 1;
}
}
cout << ans << endl;
return 0;
}