码迷,mamicode.com
首页 > 其他好文 > 详细

「解题报告」[省选联考 2020 B 卷] 丁香之路 (图论 欧拉路 最小生成树)

时间:2020-07-09 12:37:44      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:路径   需要   欧拉   its   图论   最小   最小生成树   abs   mat   

「解题报告」[省选联考 2020 B 卷] 丁香之路 (图论 欧拉路 最小生成树)

传送??

题意

一张有 \(n\) 个节点的无向图, 点 \(i,j\) 之间的边权为 \(|i-j|\). ($ n \le 2500$)

\(m\) 条必须经过的边. \((m \le \frac{n(n-1)}{2})\)

分别求出以 \(s\) 为起点 (\(s\) 给定), t (\(1 \le t \le n\)) 为终点的最短路径长.


思路

\(s\) 为起点, \(t\) 为终点, 经过 \(m\) 条必经边的路径, 可以看作一条从 \(s\)\(t\) 的 "特殊的欧拉路".

根据欧拉路的性质, 这条路径上除了 \(s,t\) 以外的点的度数都是偶数.

先从 \(s\)\(t\) 连一条路径, 考虑加上一条必经边后路径该怎么变化.

假设这条必经边为 \((u,v),u < v\) , 那么点 \(u,v\) 的度数会发生变化. 为了保证这条 "欧拉路" 的性质, 我们应该把边 \((u,u+1)\)\((v-1,v)\) 在路径中删去. 删去后, 点 \(u+1,v-1\) 的度数又会发生变化. 以此类推, 我们应该把 \((u,v)\) 范围内的所有非必经边删去. 而若 \((u,v)\) 范围内有一条空缺的非必经边, 我们又一个将它加入到路径中.

所以总的来说, 每加入一条必经边 \((u,v)\), 我们需要将 \((u,v)\) 范围内的非必经边状态取反.

对于所有必经边, 我们都这样操作后, 会把原图分为几个连通块, 那么最后我们只需要跑一遍 \(kruskal\) 将这些连通块用最小边权串起来就行了. 注意最后连通块之间的边权要乘以 \(2\) (因为包括了一来一回).

在上述过程中, 我们始终保证了 "欧拉路" 的性质, 并且保证了非必须边之间不会交叉, 所以(感性上)可以得到这应该是最优方案.


代码

#include<bits/stdc++.h>

using namespace std;

const int _=2.5e3+7;
const int __=1e7+7;
const int inf=0x3f3f3f3f;

int n,m,s,val,fa[_],f[_],bel[_];
int lst[_],nxt[__],to[__],tot;
bool d[_],c[_],ava[_],bri[_],vis[_],b[_];
struct edge{
	int u,v,w;
}e[_];

int gi(){
	int x=0; char c=getchar();
	while(c<‘0‘||c>‘9‘) c=getchar();
	while(c>=‘0‘&&c<=‘9‘){ x=(x<<3)+(x<<1)+c-‘0‘; c=getchar(); }
	return x;
}

void Add(int x,int y){
	nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot;
	nxt[++tot]=lst[y]; to[tot]=x; lst[y]=tot;
}

int Find(int x){ return fa[x]==x ?x :fa[x]=Find(fa[x]); }

void Init(){
	n=gi(),m=gi(),s=gi();
	for(int i=1;i<=n;i++)
		fa[i]=i;
	
	int x,y;
	for(int i=1;i<=m;i++){
		x=gi(),y=gi();
		Add(x,y);
		c[x]^=1,c[y]^=1;
		val+=abs(x-y);
		fa[Find(y)]=Find(x);
		bri[x]=bri[y]=1;
	}

	for(int i=1;i<=n;i++)
		f[i]=Find(i);

}

void Dfs(int u,int &num){
	vis[u]=1,num++;
	int fu=Find(u);
	if(ava[u]&&u<n&&!vis[u+1]) fa[Find(u+1)]=fu,Dfs(u+1,num);
	if(ava[u-1]&&u>1&&!vis[u-1]) fa[Find(u-1)]=fu,Dfs(u-1,num);
}

bool cmp(edge a,edge b){ return a.w<b.w; }

int Calc(int t){
	memset(vis,0,sizeof(vis));
	memset(b,0,sizeof(b));

	int ne=0,res=val;
	
	for(int i=1;i<=n;i++)
		d[i]=c[i],fa[i]=f[i];
	d[s]^=1,d[t]^=1;
	for(int i=1;i<=n;i++){
		ava[i]=ava[i-1]^d[i];
		if(ava[i]) res++;
	}
	
	for(int i=1;i<=n;i++)
		if(!vis[i]){
			int num=0;
			Dfs(i,num);
			if(num==1&&i!=s) vis[i]=0;
		}
	int lst=0,p=0;
	for(int i=1;i<=n;i++)
		if(vis[i]||bri[i]){
			int fi=Find(i);
			if(lst&&lst!=fi) e[++ne]={lst,fi,i-p};
			lst=fi,p=i;
		}

	sort(e+1,e+1+ne,cmp);
	for(int i=1;i<=ne;i++){
		int fu=Find(e[i].u),fv=Find(e[i].v);
		if(fu==fv) continue;
		res+=e[i].w+e[i].w;
		fa[fv]=fu;
	}

	return res;
}

void Run(){
	for(int t=1;t<=n;t++)
		printf("%d ",Calc(t));
	putchar(‘\n‘);
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("x.in","r",stdin);
	freopen("x.out","w",stdout);
#endif
	Init();
	Run();
	return 0;
}

「解题报告」[省选联考 2020 B 卷] 丁香之路 (图论 欧拉路 最小生成树)

标签:路径   需要   欧拉   its   图论   最小   最小生成树   abs   mat   

原文地址:https://www.cnblogs.com/BruceW/p/13272784.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!