【洛谷P6628】丁香之路
阿新 • • 發佈:2021-08-23
題目
題目連結: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 的朋友們對丁香花十分感興趣,因此他們都希望遍歷所有
Yazid 的朋友們從頂點 \(s\) 出發。其中,第 \(i\) 個朋友希望以頂點 \(i\) 為終點終止他的參觀。與此同時,如上面所述,每個朋友都必須經過開著丁香花的 \(m\) 條道路各至少一次。
Yazid 的朋友不想太過疲累,因此他們希望花盡可能少的時間來完成他們的目標。
請你計算 Yazid 的朋友們分別需要花費多少單位時間完成他們的目標。
\(n\leq 2500,m\leq \frac{n(n-1)}{2}\)。
思路
首先因為這 \(m\) 條邊是必須走的,所以可以直接讓答案加上這些邊的權值。那麼我們的目的就是最小化其他路徑的權值之和。
對於一條 \(i\to j\)
存在尤拉路徑的充要條件是起點和終點的度數為奇數,其他點的度數為偶數。那麼我們先讓 \(s\) 和 \(t\) 的度數都加一。依次列舉 \(i\),如果此時 \(i\) 的度數是奇數,那麼就連上 \(i\to i+1\) 這一條邊。
這樣貪心選擇後的代價肯定是最小的。但是現在選出的路徑可能並不連通。用並查集縮點,再次列舉所有點 \(i\),記上一個度數不為 \(0\)
時間複雜度 \(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;
}