首页 > 技术文章 > [hdu 6184 Counting Stars(三元环计数)

jiachinzhao 2017-09-04 17:53 原文

hdu 6184 Counting Stars(三元环计数)

题意:

给一张n个点m条边的无向图,问有多少个\(A-structure\)
其中\(A-structure\)满足\(V=(A,B,C,D)\) && \(E=(AB,BC,CD,DA,AC)\)

显然\(A-structure\)是由两个有公共边的三元环构成的
\(1 <=n <= 1e5\)
\(1 <= m <= min(2e5,n*(n-1)/2)\)

思路:

三元环计数
做法1、
①统计每个点的度数
②入度\(<=sqrt(m)\)的分为第一类,入度\(>sqrt(m)\)的分为第二类
③对于第一类,暴力每个点,然后暴力这个点的任意两条边,再判断这两条边的另一个端点是否连接
因为\(m\)条边最多每条边遍历一次,然后暴力的点的入度\(<=sqrt(m)\),所以复杂度约为\(O(msqrt(m))\)
④对于第二类,直接暴力任意三个点,判断这三个点是否构成环,因为这一类点的个数不会超过\(sqrt(m)\)个,所以复杂度约为\(O(sqrt(m)^3)=O(msqrt(m))\)
⑤判断两个点是否连接可以用set,map和Hash都行,根据具体情况而行
这种做法建的是双向边,常数很大

更优的做法2、建有向边 复杂度为\(O(msqrt(m))\)
对所有边按照两端点的度数定向,度数小的往度数大的走,度数相同则按编号小到大走,这样定向后
可以保证是个有向无环图。
为什么呢,要想定向存在环,则这个环上的点度数必须相同,由于保证了编号从小到大走
所以是不可能存在环的。
这样定向同时还保证了每个点的出度不超过\(sqrt(m)\),很容易证明,如果存在一个点出度超过了\(sqrt(m)\),则说明存在其他\(sqrt(m)\)个点的度数\(>sqrt(m)\),算起来超过边数\(m\)了。

对于这道题,我们在求三元环的时候,统计一下每条边有多少对点能构成三元环,\(C(cnt,2)\)累计一下即可

做法1、

#include<bits/stdc++.h>
#define LL long long

using namespace std;
void read(int &x){
    x = 0;
    char c = getchar();
    while(c < '0' || c > '9')  c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
}
const int N = 1e5 + 10;
set<LL> g;
int deg[N];
vector<int> G[N];
int vis[N],vi[N];
int main(){

    int  n, m, u, v, Sz;
    while(scanf("%d%d",&n,&m) != EOF){
        Sz = sqrt(m + 0.5);
        g.clear();
        for(int i = 1;i <= n;i++){
            vis[i] = vi[i] = deg[i] = 0;
            G[i].clear();
        }
        for(int i = 0;i < m;i++){
            scanf("%d%d",&u,&v);
            g.insert(u + 1LL * v * n);
            g.insert(v + 1LL * u * n);
            deg[u]++,deg[v]++;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        LL ans = 0;
        for(int u = 1;u <= n;u++){
            vis[u] = 1;
            for(auto v:G[u]) vi[v] = u;
            for(auto v:G[u]){
                int cnt = 0;
                if(vis[v]) continue;
                if(deg[v] <= Sz){
                    for(auto vv:G[v]){
                        if(vi[vv] == u) cnt++;
                    }
                }else{
                    for(auto vv:G[u]){
                        if(g.find(1LL * v * n + vv) != g.end()) cnt++;
                    }
                }
                ans += 1LL * cnt * (cnt - 1) / 2;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

做法二、

#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
using namespace std;
void read(int &x){
    x = 0;
    char c = getchar();
    while(c < '0' || c > '9')  c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar();
}
const int N = 1e5 + 10;
set<LL> g;
int deg[N];
vector<pair<int,int> > G[N];
int vi[N];
int X[N * 2],Y[N * 2],cnt[N],pos[N];
int main(){
    int  n, m, u, v, Sz;
    while(scanf("%d%d",&n,&m) != EOF){
        Sz = sqrt(m);
        for(int i = 1;i <= n;i++){
            vi[i] = deg[i] = pos[i] = 0;
            G[i].clear();
        }
        g.clear();
        int tot = 0;
        for(int i = 0;i < m;i++){
            scanf("%d%d",&X[i],&Y[i]);
            u = X[i],v = Y[i];
            deg[u]++,deg[v]++;
        }
        for(int i = 0;i < m;i++){
            cnt[i] = 0;
            if(deg[X[i]] < deg[Y[i]]) G[X[i]].push_back(make_pair(Y[i],i));
            else if(deg[Y[i]] < deg[X[i]]) G[Y[i]].push_back(P(X[i],i));
            else{
                if(X[i] < Y[i]) G[X[i]].push_back(P(Y[i],i));
                else G[Y[i]].push_back(P(X[i],i));
            }
        }
        LL ans = 0;
        for(int i = 0;i < m;i++){
            u = X[i],v = Y[i];
            for(auto vp:G[u]) pos[vp.first] = vp.second,vi[vp.first] = i + 1;
            for(auto vp:G[v]){
                int vv = vp.first;
                if(vi[vv] == i + 1){
                    cnt[i]++;
                    cnt[pos[vv]]++;
                    cnt[vp.second]++;
                }
            }
        }
        for(int i = 0;i < m;i++) ans += 1LL * cnt[i] * (cnt[i] - 1) / 2;
        printf("%lld\n",ans);
    }
    return 0;
}

推荐阅读