P1989 無向圖三元環計數

纯粹的發表於2024-07-27

原題連結

題解

暴力方法:

遍歷每個節點,遍歷每個節點的子節點,遍歷每個子節點的子節點,看看子子節點是否是節點的子節點,時間複雜度 \(O(nm^2)\)

最佳化

考慮無向邊建邊的時候建成有向邊,且兩個點建邊時,度數小的指向度數大的,如果度數相等,編號小的指向編號大的(其實這一步是為了避免重複計數)(啟發式建邊???)時間複雜度 \(O(m\sqrt{m})\)

證明:

每個節點,最多有 \(\sqrt{m}\) 條出邊,如果原始無向圖中,某個點有 \(k\) 條邊

  • 如果 \(k<\sqrt{m}\) 那麼其出邊也不會超過 \(\sqrt{m}\)

  • 如果 \(k\geq \sqrt{m}\) 那麼其只會指向邊比它更多的點,這樣的點不超過 \(\sqrt{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;
}


相關文章