题目描述

给定一个无向图,问这个图中是否存在三元环,或者这个图的补图中是否存在三元环。

正解

三元环计数有一个很经典的根号分治的做法,这里不详细讲,网上博客讲得很清楚。

补图的边是 级别的,就不能三元环计数了,怎么办?

由于边很多,随机三个点找到三元环的概率很大,直接随他大概 次就行。

时间复杂度 。(

代码

#include <bits/stdc++.h>
#define N 50005

using namespace std;

int n, m;
int x[N], y[N], deg[N];
int p[N], tot;

vector<int> g[N];
unordered_map<int, int> f[N];

inline int read() {
    int x = 0; char ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x;
}

int main() {
    srand(time(0));
    int Case = read();
    while(Case--) {
        n = read(), m = read();

        for(int i = 1; i <= n; ++i) {
            vector<int>().swap(g[i]);
            f[i].clear();        
        }

        for(int i = 1; i <= m; ++i) {
            x[i] = read(), y[i] = read();
            g[x[i]].push_back(y[i]), f[x[i]][y[i]] = true;
            g[y[i]].push_back(x[i]), f[y[i]][x[i]] = true;
        }

        tot = 0;
        int bl = sqrt(m);
        for(int i = 1; i <= n; ++i) {
            if(g[i].size() > bl) {
                p[++tot] = i;
            } else {
                for(auto v1 : g[i])
                    for(auto v2 : g[i])
                        if(v1 != v2 && f[v1].count(v2))
                            goto found;
            }
        }

        for(int i = 1; i <= tot; ++i)
            for(int j = i + 1; j <= tot; ++j)
                for(int k = j + 1; k <= tot; ++k)
                    if(f[i].count(j) && f[j].count(k) && f[k].count(i))
                        goto found;

        for(int t = 1, x, y, z; t <= 500000; ++t) {
            x = rand() % n + 1, y = rand() % n + 1, z = rand() % n + 1;
            if(x == y || x == z || y == z) continue;
            if(!f[x].count(y) && !f[y].count(z) && !f[z].count(x))
                goto found;
        }

        puts("no");
        continue;
        found : 
        puts("yes");
    }
    return 0;
}