【洛谷P6628】丁香之路

题目

题目链接:https://www.luogu.com.cn/problem/P6628
春暖花开,万物复苏,随着疫情的逐渐过去,Yazid 带着他的 (n) 个好朋友来到 T 大校园参观游览。方便起见,我们将他们从 (1)(n) 编号。
T 大校园的版图可以抽象成一张 (n) 个顶点的无向图(顶点编号从 (1)(n))。且对于任意两个不同顶点,设它们的编号分别为 (i, j(i eq j)),则它们之间有一条需要花费 (|i - j|) 单位时间通过的无向边。
丁香花是 T 大的校花之一。时下正值丁香花盛开之际,校园内的 (m) 条道路上都开有丁香花。Yazid 的朋友们对丁香花十分感兴趣,因此他们都希望遍历所有开有丁香花的 (m) 条道路。
Yazid 的朋友们从顶点 (s) 出发。其中,第 (i) 个朋友希望以顶点 (i) 为终点终止他的参观。与此同时,如上面所述,每个朋友都必须经过开着丁香花的 (m) 条道路各至少一次。
Yazid 的朋友不想太过疲累,因此他们希望花尽可能少的时间来完成他们的目标。
请你计算 Yazid 的朋友们分别需要花费多少单位时间完成他们的目标。
(nleq 2500,mleq frac{n(n-1)}{2})

思路

首先因为这 (m) 条边是必须走的,所以可以直接让答案加上这些边的权值。那么我们的目的就是最小化其他路径的权值之和。
对于一条 (i o j)(i<j))的路径,其实可以看作是 (i o i+1 o cdots o j)。对于终点是 (t) 的询问,也就是需要我们添加若干这样长度为 (1) 的边,使得存在一条起点是 (s),终点是 (t) 的欧拉路径。
存在欧拉路径的充要条件是起点和终点的度数为奇数,其他点的度数为偶数。那么我们先让 (s)(t) 的度数都加一。依次枚举 (i),如果此时 (i) 的度数是奇数,那么就连上 (i o 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;
}
原文地址:https://www.cnblogs.com/stoorz/p/15177750.html