把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷6628】[省选联考 2020 B 卷] 丁香之路(欧拉路)

点此看题面

  • 给定一张\(n\)个点的图,满足任意两点\(i,j\)之间存在一条边权为\(|i-j|\)的无向边。
  • 给定一个起点\(s\)以及\(m\)条必须经过的边,问对于每个终点\(t=1\sim n\),从\(s\)\(t\)在经过所有必须经过的边的前提下的最短路。
  • \(n\le2500\)

欧拉路+并查集

我们枚举一个\(t\),那么就是要在原图(强制经过的边)的基础上加上若干条边,满足存在一条从\(s\)\(t\)恰好经过所有边的路径。

这等价于满足所有有边的点都连通,且存在从\(s\)\(t\)欧拉路(即只有\(s,t\)度数为奇数,其余点度数都是偶数)。

我们可以在一开始就预先把强制给定的边全部表示到图上去,这一部分的复杂度是\(O(m)\)的。

接下来在已知\(t\)的时候,我们首先需要满足所有点度数都是偶数(对于起点和终点先给度数加上\(1\),那么也变成要满足是偶数了),这只要对所有需要修改度数的点两两配对即可(显然需要修改度数的总点数必然是偶数)。

然后我们还需要满足连通,暴力的方式就是枚举所有连通块建图跑最小生成树。

但由于此题边权的特殊性,实际上这里的最小生成树只需要考虑在相邻两点之间的连边,那么复杂度就正确了。

代码:\(O(n^2logn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 2500
#define LL long long
using namespace std;
int n,m,s,a[N+5],d_[N+5],f_[N+5],d[N+5],f[N+5];I int fa(CI x) {return f[x]^x?f[x]=fa(f[x]):x;}//并查集
int ee;struct edge {int x,y;I edge(CI a=0,CI b=0):x(a),y(b){}
	I bool operator < (Con edge& o) Con {return y-x<o.y-o.x;}}e[N+5];//按边权排序
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int main()
{
	RI i,j,k,x,y;LL t_=0;for(read(n,m,s),i=1;i<=n;++i) f[i]=i;
	for(i=1;i<=m;++i) read(x,y),t_+=abs(x-y),++d[x],++d[y],f[fa(x)]=fa(y);//把强制选择的边表示到图上
	RI p;LL t;for(i=1;i<=n;++i) d_[i]=d[i],f_[i]=f[i];for(i=1;i<=n;++i)
	{
		for(t=t_,j=1;j<=n;++j) d[j]=d_[j],f[j]=f_[j];++d[s],++d[i];//复制信息,预先给起点终点度数加1
		for(p=0,j=1;j<=n;++j) if(d[j]&1) if(!p) p=j;else for(t+=j-(k=p),p=0;k^j;++k) f[fa(k)]=fa(j);//让度数都是偶数
		for(p=ee=0,j=1;j<=n;++j) d[j]&&(p&&(e[++ee]=edge(p,j),0),p=j);//相邻两点建边
		for(sort(e+1,e+ee+1),j=1;j<=ee;++j) (x=fa(e[j].x))^(y=fa(e[j].y))&&(t+=e[j].y-e[j].x<<1,f[x]=y);//最小生成树
		printf("%d%c",t," \n"[i==n]);
	}return 0;
}
posted @ 2021-03-16 20:37  TheLostWeak  阅读(57)  评论(0编辑  收藏  举报