Description
掌管着世界的暗流的是一个叫做Samjia的人。
他看到所有人的生死,他看见所有人一世又一世的轮回,而他却从未把握过自己的命。
在无法估计的命中,他看见那些轮回,他很好奇,这一切的一切,都是如何开始如何结束,他想,就算是他也会堕入这样的轮回中的吧。
于是他开始数轮回,他看到的是一个有n个点m条边的无向图(边是带标号的),一个轮回是一个由四条边组成的环,环中不能有重复的边,除了起点和终点外(当然,由于是一个环,起点和终点是一样的)不能经过一个同样的点多次。两个轮回被视为不同的当且仅当两个轮回各自的4条边中有一条的编号不同,Samjia想知道,这个system中有多少不同的轮回。
他忙着思考人生,所以数轮回的任务就交给你了。
Solution
考场上只想出了O(N2)的算法,结果不少人靠着这个算法水过了……
我们知道当一个点只枚举度数不小于自己的点时,其枚举数量是小于2M−−−√的。
证明:设一个点的度数为x,那么在最坏情况下,度数不必它小的点的度数均为x,那么就有x2<=2∗M,x<=2M−−−√。
为了枚举不重复不遗漏,我们按照度数算出优先级,枚举一个起点x,然后枚举优先级大于x的点y,然后枚举y上优先级大于x的点z,给z的数量+1,每次算完一个起点x后结算一次答案。因为这样的循环要求枚举的起点必为循环中优先级最小的,所以这样就保证了枚举不会重复计算。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=2e5+5;
struct code{
int x,y;
}a[maxn];
ll first[maxn],last[maxn],next[maxn],d[maxn],p[maxn],bz[maxn],c[maxn];
ll n,m,i,t,j,k,l,x,y,z,ans,num,ans1;
void lian(int x,int y){
last[++num]=y;next[num]=first[x];first[x]=num;
}
bool cmp(code x,code y){
return x.x<y.x || x.x==y.x && d[x.y]<d[y.y];
}
bool cmp1(ll x,ll y){
return d[x]>d[y];
}
int main(){
freopen("palingenesis.in","r",stdin);freopen("palingenesis.out","w",stdout);
scanf("%lld%lld",&n,&m);
for (i=1;i<=m;i++)
scanf("%lld%lld",&x,&y),d[x]++,d[y]++,a[++num].x=x,a[num].y=y,a[++num].x=y,a[num].y=x;
for (i=1;i<=n;i++)c[i]=i;
sort(c+1,c+n+1,cmp1);
for (i=1;i<=n;i++)
d[c[i]]=i;
sort(a+1,a+num+1,cmp);j=1;num=0;
for (i=1;i<=n;i++)
while (a[j].x==i) lian(i,a[j++].y);
for (x=1;x<=n;x++){
for (t=first[x];t;t=next[t]){
if (d[last[t]]<=d[x]) break;
y=last[t];
for (k=first[y];k;k=next[k]){
if (d[last[k]]<=d[x]) break;
if (bz[last[k]]!=x){
ans+=p[last[k]]*(p[last[k]]-1)/2;p[last[k]]=1;bz[last[k]]=x;
}else p[last[k]]++;
}
}
}
for (i=1;i<=n;i++)
ans+=p[i]*(p[i]-1)/2,p[i]=0;
printf("%lld\n",ans);
}

本文介绍了一个关于图论的问题,即在一个无向图中计算不同四边环的数量,并提供了一种有效的算法实现。该算法通过优先级排序和枚举来避免重复计算。

1696

被折叠的 条评论
为什么被折叠?



