P1989 无向图三元环计数

原题链接

题解

暴力方法:

遍历每个节点,遍历每个节点的子节点,遍历每个子节点的子节点,看看子子节点是否是节点的子节点,时间复杂度 O(nm2)

优化

考虑无向边建边的时候建成有向边,且两个点建边时,度数小的指向度数大的,如果度数相等,编号小的指向编号大的(其实这一步是为了避免重复计数)(启发式建边???)时间复杂度 O(mm)

证明:

每个节点,最多有 m 条出边,如果原始无向图中,某个点有 k 条边

  • 如果 k<m 那么其出边也不会超过 m

  • 如果 km 那么其只会指向边比它更多的点,这样的点不超过 m

所以相当于遍历每个有向边,然后遍历终点的出边,查看是否也是起点的一条出边的终点(因为不存在有向三元环,这样也避免了重复计数)

code

#include<bits/stdc++.h>
#define ll long long
using namespace std;

vector<int> G[100005];
int out[100005]={0};
int vis[100005]={0};
int a[200005]={0};//edge
int b[200005]={0};
int du[100005]={0};

void solve()
{
    int n,m;
    cin>>n>>m;

    for(int i=1;i<=m;i++)
    {
        cin>>a[i]>>b[i];

        du[a[i]]++;
        du[b[i]]++;
    }

    for(int i=1;i<=m;i++)
    {
        int x=a[i],y=b[i];
        if(du[x]>du[y]) G[y].push_back(x);
        else if(du[x]<du[y]) G[x].push_back(y);
        else if(x<y) G[x].push_back(y);
        else G[y].push_back(x);
    }

    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        for(auto it:G[i]) vis[it]=i;

        for(auto next:G[i])
        {
            for(auto nnext:G[next])
            {
                if(vis[nnext]==i) ans++;
            }
        }
    }

    cout<<ans;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}


posted @   纯粹的  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示