原題連結
題解
暴力方法:
遍歷每個節點,遍歷每個節點的子節點,遍歷每個子節點的子節點,看看子子節點是否是節點的子節點,時間複雜度 \(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;
}