@hdu - 6184@ Counting Stars


@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\n", 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\n", ans);
    }
}

@details@

感觉这个重构成 DAG 的过程以及之后正确时间复杂度的分析奥妙重重。。。

这个思路如果拓展开来的话,应该还可以有一些很有趣的应用吧(比如「SDOI2018」旧试题)。

posted @ 2020-01-21 21:30  Tiw_Air_OAO  阅读(...)  评论(...编辑  收藏