题意: 给一张图,n(n<1e5),个点,m(m<2e5)条边,问能组成多少个,以同一条边构成的两个三元环。
思路:暴力枚举每一条边(两端点设为x,y)
然后枚举第三个点z。
1.当du[y]<=sqrt(m),用lik记录所有与x相连的边。然后枚举与y相连的点z,判断z是否与x相连。算法时间m*(sqrt(m))。
2当du[y]>sqrt(m) ,枚举与x相连的点z,用二分在y中查找z(可以用set,耗时比二分多),每次查找时间log(g[y].size()).,由于这种点很少。每次时间与x相连的点数*log(与y相连的点数)
对于每条边枚举的三元环k,答案加上每个c(2,k)。
最终时间大概和m*log(m)。
分治。
(在POJ上提交 最优577ms,平均700ms,最差少于900ms)
ACcode
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<cmath>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=100005;
vector<int> g[N];
int n,m,du[N],lik[N];
bool vis[N];
LL ans,kk;
int main()
{
int x,y,z,B;
while(scanf("%d%d",&n,&m)!=EOF)
{
B=sqrt(m);
ans=0;
for(int i=1;i<=n;i++){g[i].clear();vis[i]=false;lik[i]=-1;du[i]=0;}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
du[x]++;
du[y]++;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++)sort(g[i].begin(),g[i].end());
for(int i=1;i<=n;i++)
{
x=i;
vis[x]=true;
for(int j=0;j<g[x].size();j++)
lik[g[x][j]]=x;
for(int j=0;j<g[x].size();j++)
{
kk=0;
y=g[x][j];
if(vis[y])continue;
if(du[y]<=B)
{
for(int k=0;k<g[y].size();k++)
{
z=g[y][k];
if(lik[z]==x)
kk++;
}
}
else
{
for(int k=0;k<g[x].size();k++)
{
z=g[x][k];
if(z!=x&&binary_search(g[y].begin(),g[y].end(),z))
kk++;
}
}
ans=ans+(kk-1)*kk/2;
}
}
printf("%lld\n",ans);
}
return 0;
}
赛时代码改(800ms)
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
typedef long long LL;
const int INF=0x3f3f3f3f;
const int N=100010;
vector<int> g[N];
int a[N*2],b[N*2],n,m,du[N],B;
LL ans,k;
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
ans=0;
B=sqrt(m);
for(int i=1;i<=n;i++){g[i].clear();du[i]=0;}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
g[a[i]].push_back(b[i]);
g[b[i]].push_back(a[i]);
du[a[i]]++;
du[b[i]]++;
}
for(int i=1;i<=n;i++)
sort(g[i].begin(),g[i].end());
int x,y,xi,yi,z;
for(int i=1;i<=m;i++)
{
x=a[i];y=b[i];
xi=0;
yi=0;
k=0;
if(du[y]<=B&&du[x]<=B)
while(xi<g[x].size()&&yi<g[y].size())
{
if(g[x][xi]>g[y][yi])
yi++;
else if(g[x][xi]<g[y][yi])
xi++;
else
{//cout<<x<<" "<<y<<" "<<g[x][xi]<<endl;
xi++;yi++;
k++;
}
}
else if(du[x]<=B)
for(int j=0;j<g[x].size();j++)
{
z=g[x][j];
if(binary_search(g[y].begin(),g[y].end(),z))
k++;
}
else
for(int j=0;j<g[y].size();j++)
{
z=g[y][j];
if(binary_search(g[x].begin(),g[x].end(),z))
k++;
}
ans=ans+k*(k-1)/2;
}
printf("%lld\n",ans);
}
return 0;
}