zoukankan      html  css  js  c++  java
  • 【科技】三/四元环计数

    【科技】三元环计数

    介绍一个小科技,三元环计数,利用复杂度分析证明暴力求解是科学的。

    具体问题就是,给定一张 n 个点,m 条边的简单无向图,求解无序三元组 (i,j,k)的数量,其中满足存在边 (i,j),(i,k),(j,k)

    分析:

    第一秒想到的是直接暴力枚举,但发现一个环上每个点都会把这个环算上一遍,纵使最后容斥除掉

    但每个点为基础找三元环的复杂度也是很高的

    考虑怎么才能让一个环只被算上一次,这样是最优化的了,再没有别的方法了

    正解:

    我们先把无向图转成有向图

    给每个点定义一个双关键字(deg,id),其中deg表示度数,id表示标号

    这样对于每一对点都能严格比较出大小

    我们把每一条边重定向成从度数大的点连向度数小的点,我们就可以得到一张有向无环图。

    具体怎么找环:

    1. 枚举一个点i,将所有i点连出的点标记为i
    2. 枚举一个点i连出的点j
    3. 枚举一个点j连出的点k,如果k的标记是i,那么就找到了一组三元环(i,j,k)

    分析每一个三元环只会在i这个点被算到一次答案

    可以证明这么做的复杂度的一个上界是O(m√m)

    因为一个三元环在新图上必定只有一个点的出度为2,然后我们只在这个点上更新三元环数量

    code :

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 250005;
    int n, m, ans;
    int a[N], du[N], vi[N], eu[N], ev[N];
    vector<int> g[N];
    inline bool cmp(int x, int y) {
      return du[x] != du[y]? du[x] > du[y] : x < y;
    }
    int main() {
      scanf("%d%d", &n, &m);
      for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);
      for (int i = 1; i <= m; ++i)
          scanf("%d%d", &eu[i], &ev[i]), ++du[eu[i]], ++du[ev[i]];
      for (int i = 1; i <= m; ++i)
        if (cmp(eu[i], ev[i])) g[eu[i]].push_back(ev[i]);
        else g[ev[i]].push_back(eu[i]);
      for (int i = 1; i <= n; ++i) {
        for (int j : g[i]) vi[j] = i;
        for (int j : g[i])
          for (int k : g[j])
            if (vi[k] == i) ++ans;
      }
      printf("%d
    ", ans);
      return 0;
    }
    

    那四元环怎么办?

    类似三元环还是要对边定向。

    但此时注意枚举点 u 相邻的点 v 是原图中的边(而非重定向后的边),

    而枚举 v相邻的点 w 则要是重定向后的点(可以交换图的顺序),

    原因是我们相当于是枚举两个部分拼起来。

    还是在 u 被枚举一次,因为 rk[u]<rk[w]

    code:

    inline bool cmp(const int& _this,const int& _that) 
    	{return d[_this]<d[_that]||(d[_this]==d[_that]&&_this<_that);}
    #define con const int&
    inline void main() {
    	n=g(),m=g(); for(R i=1,u,v;i<=m;++i) 
    		u=g(),v=g(),e[u].push_back(v),e[v].push_back(u);
    	for(R i=1;i<=n;++i) d[id[i]=i]=e[i].size();
    	sort(id+1,id+n+1,cmp);
    	for(R i=1;i<=n;++i) rk[id[i]]=i;
    	for(R u=1;u<=n;++u) for(con v:e[u]) 
    		if(rk[v]>rk[u]) f[u].push_back(v);
    	for(R u=1;u<=n;++u) {
    		for(con v:e[u]) for(con w:f[v]) if(rk[w]>rk[u]) ans+=c[w],++c[w]; //交换e与f的枚举顺序也是对的。
    		for(con v:e[u]) for(con w:f[v]) if(rk[w]>rk[u]) c[w]=0; //清空桶。
    	} printf("%lld
    ",ans);
    } 
    
    
    
  • 相关阅读:
    mongodb
    python中读取文件的read、readline、readlines方法区别
    uva 129 Krypton Factor
    hdu 4734
    hdu 5182 PM2.5
    hdu 5179 beautiful number
    hdu 5178 pairs
    hdu 5176 The Experience of Love
    hdu 5175 Misaki's Kiss again
    hdu 5174 Ferries Wheel
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11792340.html
Copyright © 2011-2022 走看看