[bzoj3498]cakes(三元环)

本文介绍了一种高效寻找图中所有三元环的方法,通过预处理将双向边转换为单向边,并利用节点度数特性降低复杂度至O(m*sqrt(m))。文章详细解释了算法思路并提供了实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面
题意基本就是找出所有三元环
n<=100000m<=250000 n <= 100000 m <= 250000

我竟然才会这个鬼东西 太菜了qwq
说起来我前几天才知道中国剩余定理2333333

先把双向边,变成单向边
由度数小的点,连向度数大的点,如果度数相等 从小点连向大点

这样之后,每个点连出去的边小于 sqrt(m) s q r t ( m )

因为原本度数大于 sqrt(m) s q r t ( m ) 的点小于 sqrt(m) s q r t ( m ) 个,又因为度数大于 sqrt(m) s q r t ( m ) 只会连度数更大的,所以度数大于 sqrt(m) s q r t ( m ) 的点最多只会连出去 sqrt(m) s q r t ( m ) 条有向边。

所以只需从一个点枚举任意两条边,再判断连出去的两个点是否有边即可(hash),复杂度 O(msqrt(m)) O ( m ∗ s q r t ( m ) )

然后我hash TLE了…

换了种写法,枚举边,再枚举这条边两边的点,遍历这两个点连出去的所有点,打个标记,就能去掉hash了


代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
class Dread{
    private:
        bool isdigit(char ch) { return ch >= '0' && ch <= '9'; }
        void Getchar(int &tmp){
            char ch; tmp = 0; bool b = true;
            while (ch = getchar()){
                if (ch == '-') b = false;
                if (isdigit(ch)) break;
            }
            for (; isdigit(ch); ch = getchar()) tmp = tmp * 10 + ch - '0';
            if (!b) tmp = -tmp;
        }
    public:
        int Int(){ int x; Getchar(x); return x; }
}Read;
const int maxn = 1e5 + 10;
const int maxm = 2.5e5 + 10;
vector<int> g[maxn];
struct {
    int x, y;
}edge[maxm];
int n, m;
int a[maxn], id[maxn];
int d[maxn];
void init(){
    n = Read.Int(), m = Read.Int();
    for (int i = 1; i <= n; i ++)
        a[i] = Read.Int();
    for (int i = 1; i <= m; i ++){
        edge[i].x = Read.Int(), edge[i].y = Read.Int();
        d[edge[i].x] ++, d[edge[i].y] ++;
    }
}
bool cmp(int x, int y){
    if (d[x] != d[y]) return d[x] < d[y];
    return x < y;
}
int vis[maxn];
void work(){
    for (int i = 1; i <= m; i ++){
        if (!cmp(edge[i].x, edge[i].y))
            swap(edge[i].x, edge[i].y);
        g[edge[i].x].push_back(edge[i].y);
    }
    ll ans = 0;
    for (int i = 1; i <= m; i ++){
        int tmp = max(a[edge[i].x], a[edge[i].y]);
        for (int asd = 0; asd < g[edge[i].x].size(); asd ++)
            vis[g[edge[i].x][asd]] = i;
        for (int asd = 0; asd < g[edge[i].y].size(); asd ++)
            if (vis[g[edge[i].y][asd]] == i)
                ans += max(a[g[edge[i].y][asd]], tmp);
    }
    cout <<ans <<endl;
}
int main(){
    init();
    work();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值