Description
N个点m条边,每个点有一个点权a。
对于任意一个三元环(j,j,k)(i<j<k),它的贡献
为max(ai,aj,ak)
求所有三元环的贡献和。
N<100000,,m<250000。
对度数大的点w,枚举w相邻的每个点u1,u2,u3...,遍历u[a]相邻的点的统计w,u[a],u[b](b<a)构成三元环的情况
对度数小的点w,枚举w相邻的每个点,在另外存边的散列表中 判断它们两两之间是否有边
每个三元环恰好统计三次,两种策略时间复杂度在判断标准为$\sqrt{m}$时达到平衡
#include<cstdio> #include<vector> const int RN=1e5; char buf[RN+7],*ptr=buf+RN; int G(){ if(ptr==buf+RN)fread(ptr=buf,1,RN,stdin); return *ptr++; } int _(){ int x=0; if(ptr<buf+RN-50){ while(*ptr<48)++ptr; while(*ptr>47)x=x*10+*ptr++-48; }else{ int c=G(); while(c<48)c=G(); while(c>47)x=x*10+c-48,c=G(); } return x; } const int P=1844677,N=1e5+7,D=333; int n,m,a[N],ed[N],e[N*3][2],deg[N],*lr[N][2],mem[N*5],*mp=mem; int max(int a,int b){return a>b?a:b;} long long ans=0; int h[P][2]; void ins(int a,int b){ int w=(a*1399+b*2939)%P; while(h[w][0]){ if(h[w][0]==a&&h[w][1]==b)return; w=(w+21331)%P; } h[w][0]=a,h[w][1]=b; } bool find(int a,int b){ int w=(a*1399+b*2939)%P; while(h[w][0]){ if(h[w][0]==a&&h[w][1]==b)return 1; w=(w+21331)%P; } return 0; } int main(){ n=_(),m=_(); for(int i=1;i<=n;++i)a[i]=_(); for(int i=1;i<=m;++i){ ++deg[e[i][0]=_()]; ++deg[e[i][1]=_()]; } for(int i=1;i<=n;++i)lr[i][0]=lr[i][1]=mp,mp+=deg[i]; for(int i=1;i<=m;++i){ int a=e[i][0],b=e[i][1]; *lr[a][1]++=b; *lr[b][1]++=a; ins(a,b); ins(b,a); } for(int i=1;i<=n;++i)if(deg[i]>D){ for(int*j=lr[i][0];j!=lr[i][1];++j){ int w=*j,mx=max(a[i],a[w]); ed[w]=i; for(int*k=lr[w][0];k!=lr[w][1];++k)if(ed[*k]==i)ans+=max(mx,a[*k]); } }else{ for(int*j=lr[i][0];j!=lr[i][1];++j){ int w=*j,mx=max(a[i],a[w]); for(int*k=lr[i][0];k!=j;++k)if(find(w,*k))ans+=max(mx,a[*k]); } } printf("%lld\n",ans/3); return 0; }