1. 程式人生 > 實用技巧 >Luogu P4809 [CCC 2018]最大戰略儲備|最小生成樹

Luogu P4809 [CCC 2018]最大戰略儲備|最小生成樹

連結

題目大意:

\(N\) 個星球,編號為 \(1\ldots N\)。每個星球有 \(M\) 座城市,編號為 \(1\ldots M\)。我們將 \(e\) 星球上的城市 \(f\) 記作 \((e,\,f)\)。有兩類邊,均為雙向:
\(N\times P\) 條一類邊,每個星球有 \(P\) 條,編號為 \(1\)\(P\)。第 \(i\) 條邊連線城市 \((e,\,a_i)\)\((e,\,b_i)\) ,邊權為 \(c_i\)
\(M\times Q\) 個二類邊。每個城市有 \(Q\) 條,編號為 \(1\)\(Q\)。第 \(j\) 個邊連線城市 \((x_j,\,f)\)

\((y_j,\,f)\) ,邊權 \(z_j\)

如圖所示,紅色為一類邊,綠色為二類邊。請讀者注意,這裡極易混淆導致下文無法理解
求總邊權和-最小生成樹邊權和。

\(0\le N,M,P,Q\le 10^5\),邊權最大\(10^8\)

題目思路:

首先,我們可以按照題目大意做出最小生成樹,這樣可以拿到\(59 pts\)。而剩餘測試點會因點數或邊數過大而無法通過,因此需考慮優化。
觀察一下本題中,\(Kruskal\)演算法執行時的加邊過程,可以發現選邊時是選一組相同(即所有\(a_i\ b_i\ c_i\)或所有\(x_j\ y_j\ z_j\))的,這裡面又是有規律的。我們將一組相同的邊統籌起來,並分類考慮。

以下所有敘述均以Kruskal為基礎

若當前沒有一類邊,我們加入二類邊,顯然要加\(m\)條。
若當前有一組一類邊,那麼必定在每個星球中,存在\((e,a_i)\)\((e,b_i)\)被連線,此時二類邊可以不同時在\(i\)\(j\)連邊,而是在\(i\)\(j\)連一條邊即可,這與同時連邊是等價的。因此,僅需加\(m-1\)條邊
若當前有兩組一類邊,同理,需加\(m-2\)條邊,若當前有\(x\)組一類邊,需加\(m-x\)條二類邊
而二類邊對一類邊的影響也是類似的,若有\(y\)組二類邊,需加\(n-y\)條一類邊
有了這些規律後,我們可以發現不再需要用所有點建最小生成樹,而只需要維護一行和一列,建最小生成樹即可,即,並查集由\(n \times m\)

變為\(n+m\),這時建最小生成樹可以通過。

上程式碼

#include<bits/stdc++.h>
using namespace std;
int n,m,p,q,fa[300100],siz;long long s,s1,s2;bool task1;
struct edg
{
	long long u,v,len;
}e1[100100],e2[100100];
bool cmp(edg x,edg y)
{
	return x.len<y.len;
}
int getfa(int x)
{
	if (x==fa[x]) return x;
	return fa[x]=getfa(fa[x]);
}
bool hebing(int x,int y)
{
	x=getfa(x);y=getfa(y);
	if (x!=y) 
	{
		fa[x]=y,siz--;
		return true;
	}
	else return false;
}//並查集
int main() 
{
	cin>>n>>m>>p>>q;
	for (int i=1;i<=p;i++)
	{
		cin>>e1[i].u>>e1[i].v>>e1[i].len;
		s+=e1[i].len*n;
		if (e1[i].len!=1) task1=false;
	}
	for (int i=1;i<=q;i++)
	{
		cin>>e2[i].u>>e2[i].v>>e2[i].len;
		s+=e2[i].len*m;
		if (e2[i].len!=1) task1=false;
	}
		siz=n+m;
		for (int i=1;i<=n+m;i++)
		{
			fa[i]=i;
		}
		sort(e1+1,e1+p+1,cmp);
		sort(e2+1,e2+q+1,cmp);//排序,Kruskal建邊
		bool f1=false,f2=false;
		for (int i=1,j=1;i<=p||j<=q;)
		{
			if ((i<=p)&&(e1[i].len<e2[j].len||j>q))
			{
				f1=hebing(e1[i].u,e1[i].v);			
				if (f1) s-=e1[i].len*(n-s2),s1++;
				if (siz==2) break;
				i++;continue;
			}
			else
			{
				f2=hebing(m+e2[j].u,m+e2[j].v); 	
				if (f2) s-=e2[j].len*(m-s1),s2++;	
				if (siz==2) break;
				j++;continue;
			}
		}
		cout<<s<<endl;
	return 0; 
}