题目
思路来源
https://www.cnblogs.com/jiachinzhao/p/7474761.html
https://www.cnblogs.com/Khada-Jhin/p/10143074.html
题解
三元环的题,可参考bzoj3498
本来看了题解,也没大懂,好在归神讲了讲
只会暴力check这一种做法
对于for(u=1;u<=n;++u)来讲
①考虑所有u为度数>sqrt(m)的点,
(1)如果v为度数>sqrt(m)的点,就去for循环u,
枚举的是形如u-vv这样的边,复杂度O(m),
由于u这种点的个数不超过sqrt(m),所以复杂度是O(m*sqrt(m))的
(2)如果v为度数<sqrt(m)的点,就去for循环v,
枚举的是形如u-v这样的边,O(m)
这样由于v的度数不超过sqrt(m),保证了v的点的个数是O(sqrt(m))的,
所以复杂度是O(m*sqrt(m))的
②考虑所有u为度数<sqrt(m)的点
(1)如果v为度数>sqrt(m)的点,就去for循环u的点,
由于u的度数不超过sqrt(m),枚举的是形如u-v这样的边,所以复杂度是O(m*sqrt(m))的
(2)如果v为度数<sqrt(m)的点,就去for循环v的点,
由于v的度数不超过sqrt(m),枚举的是形如u-v这样的边,所以复杂度是O(m*sqrt(m))的
综上,均摊复杂度O(m*sqrt(m)),但常数较大
简言之,就是对于每个固定的u,v的度数小的话就检查v内的点,否则检查u内的点
由于枚举的是u-v,且v-u不会被重复枚举,所以根据交边C(cnt,2)统计即可
代码
#include<set>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
#define pb push_back
typedef long long ll;
const int N=1e5+10;
set<ll>q;
vector<int>e[N];
bool vis[N];
int n,m,u,v,sz,deg[N],vi[N];
ll ans;
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;++i)
e[i].clear(),deg[i]=vis[i]=vi[i]=0;
q.clear();
ans=0;
sz=sqrt(m+0.5);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
e[u].pb(v);e[v].pb(u);
deg[u]++;deg[v]++;
q.insert(v+1ll*u*n);
q.insert(u+1ll*v*n);
}
for(int u=1;u<=n;++u)
{
vis[u]=1;
for(auto v:e[u])
vi[v]=u;
for(auto v:e[u])//对于每对(u,v)的边 统计位于多少个三元环内
{
if(vis[v])continue;
int cnt=0;
if(deg[v]<=sz)
{
for(auto vv:e[v])
if(vi[vv]==u)cnt++;
}
else
{
for(auto vv:e[u])
if(q.find(vv+1ll*n*v)!=q.end())cnt++;
}
ans+=1ll*cnt*(cnt-1)/2;
}
}
printf("%lld\n",ans);
}
return 0;
}