1. 程式人生 > 實用技巧 >聯賽模擬測試24 C. 聯合權值,改(三元環)

聯賽模擬測試24 C. 聯合權值,改(三元環)

題目描述


分析

有一個結論:在一個有 \(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\n",mmax);
	else printf("0\n");
	if(t!=1) printf("%lld\n",ans);
	else printf("0\n");
	return 0;
}