题目描述
分析
有一个结论:在一个有 (m)条边的图中,三元环的个数为(O(m^{1.5}))的。
显然一个点数为(O(m^{0.5}))的完全图可以使得三元环个数取到这个上界,但是这是对边
的利用率最高的一种做法,你无法找到一个利用率更高的图。
本题要找的东西实际上就是由三个点两条边组成的链除去三元环的情况
对于最大值来说:我们把每一个点所连接的点按照权值从大到小排序
然后按照排好序后的顺序对于每一个点用两重循环枚举与它相邻的两个点
如果这三个点没有形成三元环,更新最大值,跳出循环
因为匹配失败一定是找到了一个三元环,而三元环个数最多为(m^{1.5})
所以效率是 (m^{1.5})的
对于总和来说,我们先求出和某个点相邻的点的权值的平方,在减去三元环和两个点重复枚举的情况
重复枚举的情况好处理,难点在于如何处理三元环
我们需要重新构图
对于每个点进行一次预处理,求出和某个点 (a) 相邻的,并且点的度数大于 (a),或者点的度数和(a)相同,但点的编号大于(a)的点
然后将从 (a) 点向该点连边构成新图
因为一个三元环有 (6) 种排列方式,而我们的操作就相当与人为地规定了其中一种排列方式
所以得到的新图一定是不重不漏的
具体实现时枚举点(u),再枚举与(u)相邻的满足上述条件的点(v),再枚举与(v)相邻的满足上述条件
的点(w),然后判断((u, v, w))是否构成三元环。如果构成三元环,则我们可以把该三元环产生的联合权值从答案中扣去
这样做复杂度也是对的
枚举边 ((u,v)) 的复杂度不会超过 (m)
对于边 ((v,w)) ,如果 (v) 的度数大于 (sqrt{m}),那么这样的点不会超过 (sqrt{m}) 个,因为 (v) 只会向度数比它大的点连边,所以 (v) 向外连的边也不会超过 (sqrt{m})条
如果 (v) 的度数小于 (sqrt{m}),那么向外连的边一定小于它的度数
所以复杂度也是 (m^{1.5})
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<vector>
#include<bitset>
#include<map>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=3e4+5;
int n,m,t,mmax=-1,w[maxn],du[maxn];
long long ans,num[maxn],cf[maxn];
std::vector<int> g[maxn],g2[maxn];
std::bitset<maxn> bit[maxn];
bool cmp(int aa,int bb){
return w[aa]>w[bb];
}
int main(){
n=read(),m=read(),t=read();
rg int aa,bb;
for(rg int i=1;i<=m;i++){
aa=read(),bb=read();
bit[aa][bb]=bit[bb][aa]=1;
g[aa].push_back(bb);
g[bb].push_back(aa);
du[aa]++,du[bb]++;
}
for(rg int i=1;i<=n;i++){
w[i]=read();
}
for(rg int i=1;i<=n;i++){
std::sort(g[i].begin(),g[i].end(),cmp);
}
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g[i].size();j++){
for(rg int k=j+1;k<g[i].size();k++){
if(bit[g[i][j]][g[i][k]]) continue;
mmax=std::max(mmax,w[g[i][j]]*w[g[i][k]]);
break;
}
}
}
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g[i].size();j++){
num[i]+=w[g[i][j]];
cf[i]+=w[g[i][j]]*w[g[i][j]];
}
num[i]=num[i]*num[i]-cf[i];
ans+=num[i];
}
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g[i].size();j++){
if(du[g[i][j]]>du[i] || (du[g[i][j]]==du[i] && g[i][j]>i)){
g2[i].push_back(g[i][j]);
}
}
}
for(rg int i=1;i<=n;i++){
for(rg int j=0;j<g2[i].size();j++){
rg int now=g2[i][j];
for(rg int k=0;k<g2[now].size();k++){
rg int cs=g2[now][k];
if(bit[i][cs]){
ans-=w[i]*w[cs]*2;
ans-=w[i]*w[now]*2;
ans-=w[now]*w[cs]*2;
}
}
}
}
if(t!=2) printf("%d
",mmax);
else printf("0
");
if(t!=1) printf("%lld
",ans);
else printf("0
");
return 0;
}