1. 程式人生 > 其它 >【洛谷P6628】丁香之路

【洛谷P6628】丁香之路

題目

題目連結:https://www.luogu.com.cn/problem/P6628
春暖花開,萬物復甦,隨著疫情的逐漸過去,Yazid 帶著他的 \(n\) 個好朋友來到 T 大校園參觀遊覽。方便起見,我們將他們從 \(1\)\(n\) 編號。
T 大校園的版圖可以抽象成一張 \(n\) 個頂點的無向圖(頂點編號從 \(1\)\(n\))。且對於任意兩個不同頂點,設它們的編號分別為 \(i, j(i\neq j)\),則它們之間有一條需要花費 \(|i - j|\) 單位時間通過的無向邊。
丁香花是 T 大的校花之一。時下正值丁香花盛開之際,校園內的 \(m\) 條道路上都開有丁香花。Yazid 的朋友們對丁香花十分感興趣,因此他們都希望遍歷所有

開有丁香花的 \(m\) 條道路。
Yazid 的朋友們從頂點 \(s\) 出發。其中,第 \(i\) 個朋友希望以頂點 \(i\) 為終點終止他的參觀。與此同時,如上面所述,每個朋友都必須經過開著丁香花的 \(m\) 條道路各至少一次。
Yazid 的朋友不想太過疲累,因此他們希望花盡可能少的時間來完成他們的目標。
請你計算 Yazid 的朋友們分別需要花費多少單位時間完成他們的目標。
\(n\leq 2500,m\leq \frac{n(n-1)}{2}\)

思路

首先因為這 \(m\) 條邊是必須走的,所以可以直接讓答案加上這些邊的權值。那麼我們的目的就是最小化其他路徑的權值之和。
對於一條 \(i\to j\)

\(i<j\))的路徑,其實可以看作是 \(i\to i+1\to \cdots \to j\)。對於終點是 \(t\) 的詢問,也就是需要我們新增若干這樣長度為 \(1\) 的邊,使得存在一條起點是 \(s\),終點是 \(t\) 的尤拉路徑。
存在尤拉路徑的充要條件是起點和終點的度數為奇數,其他點的度數為偶數。那麼我們先讓 \(s\)\(t\) 的度數都加一。依次列舉 \(i\),如果此時 \(i\) 的度數是奇數,那麼就連上 \(i\to i+1\) 這一條邊。
這樣貪心選擇後的代價肯定是最小的。但是現在選出的路徑可能並不連通。用並查集縮點,再次列舉所有點 \(i\),記上一個度數不為 \(0\)
的點是 \(j\)(因為度數為 \(0\) 的點乾脆不經過就行),如果 \(i\)\(j\) 所在的連通塊不同,那麼就增加一條長度為 \(|i-j|\) 的邊連線兩個連通塊。最後求一下最小生成樹即可。顯然所需要增加的代價就是最小生成樹權值的 \(2\) 倍。
時間複雜度 \(O((n^2+m)\log n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;

const int N=2510,M=3200010;
int n,m,s,ans,sum,deg[N],deg1[N],father1[N],father[N];

struct edge
{
	int u,v,dis;
}e[M];

void prework()
{
	memcpy(deg,deg1,sizeof(deg));
	memcpy(father,father1,sizeof(father));
	ans=sum; m=0;
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

bool cmp(edge x,edge y)
{
	return x.dis<y.dis;
}

int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for (int i=1;i<=n;i++) father[i]=i;
	for (int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		deg1[x]++; deg1[y]++;
		father[find(x)]=find(y);
		sum+=abs(x-y);
	}
	memcpy(father1,father,sizeof(father));
	for (int i=1;i<=n;i++)
	{
		prework();
		deg[s]++; deg[i]++;
		for (int j=1;j<n;j++)
			if (deg[j]&1)
			{
				ans++; deg[j]++; deg[j+1]++;
				father[find(j)]=find(j+1);
			}
		for (int j=1,k=0;j<=n;j++)
			if (deg[j])
			{
				if (k && find(j)!=find(k))
					e[++m]=(edge){find(j),find(k),j-k};
				k=j;
			}
		sort(e+1,e+1+m,cmp);
		for (int j=1;j<=m;j++)
		{
			int x=find(e[j].u),y=find(e[j].v);
			if (x!=y) father[x]=y,ans+=2*e[j].dis;
		}
		cout<<ans<<" ";
		deg[s]--; deg[i]--;
	}
	return 0;
}