@hdu


@description@

给定一个 n 点 m 边的无向图(无重边自环),求有多少子图形如,包含 4 个点 {A, B, C, D} 与 6 条边 {AB, BC, CD, DA, AC}。

原题链接。

@solution@

一个并不常用的黑科技:三元环计数。

mark一下博客地址。

注意到题目所说的子图其实就是两个三元环共一条边。
我们只需要根据三元环计数的过程统计每条边对应多少三元环,最后每条边的贡献即这条边对应的三元环任意取 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」旧试题)。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12225809.html