题目描述
掌管着世界的暗流的是一个叫做Samjia的人。
他看到所有人的生死,他看见所有人一世又一世的轮回,而他却从未把握过自己的命。
在无法估计的命中,他看见那些轮回,他很好奇,这一切的一切,都是如何开始如何结束,他想,就算是他也会堕入这样的轮回中的吧。
于是他开始数轮回,他看到的是一个有n个点m条边的无向图(边是带标号的),一个轮回是一个由四条边组成的环,环中不能有重复的边,除了起点和终点外(当然,由于是一个环,起点和终点是一样的)不能经过一个同样的点多次。两个轮回被视为不同的当且仅当两个轮回各自的4条边中有一条的编号不同,Samjia想知道,这个system中有多少不同的轮回。
他忙着思考人生,所以数轮回的任务就交给你了。
题解
好强啊!
我们来定义一下优先级吧!
两个点,度数大的优先级高。
度数一样则编号大的优先级高。
对于一个四元环,我们设k是其中优先级最大的,i与k处于同一对角线。
我们可以枚举i,对于一次i的枚举,需要一个cnt数组,初始全0,cnt[k]表示目前有多少i->j->k,其中k当然要是这里面优先级最大的。
我们接着来枚举j,(i,j)的枚举是O(m)的。
接着我们枚举k,判断k是否是优先级最大的,然后答案加上cnt[k],接着再把cnt[k]加一。
容易看出这样是正确且不会计重的,那么枚举k的复杂度是多少呢?
一个点,与它相连且度数不比它小的点的个数是
m−−√
假设一个点有x个这样的点,那么这些点度数至少为x,这样就有x^2条边。
那么可证明这个结论。
所以这个算法复杂度为
O(mm−−√)
#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int edge[maxn][2],h[maxn],go[maxn*2],next[maxn*2],d[maxn],cnt[maxn];
int h2[maxn],g2[maxn*2],n2[maxn*2];
int i,j,k,l,r,t,x,y,z,w,n,m,ans,tot,top;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y){
d[y]++;
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void add2(int x,int y){
g2[++top]=y;
n2[top]=h2[x];
h2[x]=top;
}
int main(){
freopen("palingenesis.in","r",stdin);freopen("palingenesis.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m){
edge[i][0]=j=read();edge[i][1]=k=read();
add(j,k);add(k,j);
}
fo(i,1,m){
j=edge[i][0];k=edge[i][1];
if (d[j]<d[k]||d[j]==d[k]&&j<k) add2(j,k);
if (d[k]<d[j]||d[k]==d[j]&&k<j) add2(k,j);
}
fo(i,1,n){
t=h[i];
while (t){
j=go[t];
r=h2[j];
while (r){
k=g2[r];
if (d[i]<d[k]||d[i]==d[k]&&i<k){
ans+=cnt[k];
cnt[k]++;
}
r=n2[r];
}
t=next[t];
}
t=h[i];
while (t){
j=go[t];
r=h2[j];
while (r){
k=g2[r];
if (d[i]<d[k]||d[i]==d[k]&&i<k) cnt[k]=0;
r=n2[r];
}
t=next[t];
}
}
printf("%d\n",ans);
}