@description@
给定一个 n 点 m 边的无向图(无重边自环),求有多少子图形如,包含 4 个点 {A, B, C, D} 与 6 条边 {AB, BC, CD, DA, AC}。
@solution@
一个并不常用的黑科技:三元环计数。
注意到题目所说的子图其实就是两个三元环共一条边。
我们只需要根据三元环计数的过程统计每条边对应多少三元环,最后每条边的贡献即这条边对应的三元环任意取 2 个的方案数。
@accepted code@
#include <cstdio>
typedef long long ll;
const int MAXN = 100000;
const int MAXM = 200000;
struct edge{
int to, id;
edge *nxt;
}edges[MAXM + 5], *adj[MAXN + 5], *ecnt;
void addedge(int u, int v, int i) {
edge *p = (++ecnt);
p->to = v, p->id = i, p->nxt = adj[u], adj[u] = p;
// printf("%d %d : %d
", u, v, i);
}
int n, m;
int deg[MAXN + 5], u[MAXM + 5], v[MAXM + 5];
ll f[MAXM + 5];
void clear() {
for(int i=1;i<=n;i++) adj[i] = NULL, deg[i] = 0;
for(int i=1;i<=m;i++) f[i] = 0;
ecnt = edges;
}
int tag[MAXN + 5];
int main() {
while( scanf("%d%d", &n, &m) == 2 ) {
clear();
for(int i=1;i<=m;i++)
scanf("%d%d", &u[i], &v[i]), deg[u[i]]++, deg[v[i]]++;
for(int i=1;i<=m;i++)
if( deg[u[i]] > deg[v[i]] || (deg[u[i]] == deg[v[i]] && u[i] > v[i]) )
addedge(u[i], v[i], i);
else addedge(v[i], u[i], i);
for(int i=1;i<=n;i++) {
for(edge *p=adj[i];p;p=p->nxt) tag[p->to] = p->id;
for(edge *p=adj[i];p;p=p->nxt)
for(edge *q=adj[p->to];q;q=q->nxt)
if( tag[q->to] ) f[p->id]++, f[q->id]++, f[tag[q->to]]++;
for(edge *p=adj[i];p;p=p->nxt) tag[p->to] = 0;
}
ll ans = 0;
for(int i=1;i<=m;i++)
ans += f[i]*(f[i] - 1)/2;
printf("%lld
", ans);
}
}
@details@
感觉这个重构成 DAG 的过程以及之后正确时间复杂度的分析奥妙重重。。。
这个思路如果拓展开来的话,应该还可以有一些很有趣的应用吧(比如「SDOI2018」旧试题)。