[BZOJ2887] 旅行

一、题目

点此看题

二、解法

如果第二张图是欧拉图,那么可以通过两次走 ((u,v)) 经过所有边恰好一次,因为无向连通图的欧拉路可以理解成原图的环拆分,我们以包含 ((u,v)) 的大环为骨架就可以构造出方案。对于第一张图我们可以直接 ( t dfs) 原图获得一个经过所有边两次的方案,设第二张图的边权和是 (sum),显然得到的权值是 (mcdot sum) 而且它是答案上界。

否则分两种情况讨论,设欧拉路的两个端点是 (A,B),那么走一次的最大权值是总边权减去四个点任意配对的最短路:(sum-min(d[u][A]+d[v][B],d[u][v]+d[A][B],d[u][B]+d[v][A]))

首先证明关键引理:存在最优路径经过所有边(无论奇数次还是偶数次),它的证明极其简单,我们可以从一个最优解开始调整,也就是从终点开始走一条路径再原路折返。

这说明我们可以从欧拉路径开始调整(因为其经过所有边),我们先让路径中已有一条欧拉路径,那么问题变成了走到欧拉路径的起点,再从欧拉路径的终点走到终点的最小路径(奇数算贡献),不难构造出三种两两配对的方案。

走两次的权值是 (sum-d[A][B]),也就是 ((A,B)) 之间的最短路,和起终点没有关系了:

我们经过一条边几次就拆出来几条重边,那么可以把原路径转化成新图的欧拉路。我们可以发现存在最优答案经过每条边至多两次,否则我们可以调整使得权值不变(度数同时减少 (2) 还是存在欧拉路)

那么问题变成了增加一条重边有边权的花费,问使半欧拉图变成欧拉图的最小花费。显然增加的边必须构成 (A ightarrow B) 的一条路径,才能满足度数都是偶数的条件,那么取最短路就得到了最小花费。

注意到 (mleq n+5)(题面有误没有说),我们可以枚举非树边的经过次数,如果一条非树边经过奇数次就会导致树上对应的环要走一次,那么我们可以用一遍树上差分求出每条边经过次数的奇偶性就得到了答案,时间复杂度 (O(ncdot 2^{6}))

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 100005;
const int inf = 0x3f3f3f3f;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,p,q,A,B,a[M],vis[M],b[M],d[M],ea[M],eb[M];
vector<int> g1[M],g[M];ll res,sum,ans,f[205][205];
ll once(int u,int v)
{
	u=a[u];v=a[v];
	return sum-min(f[u][A]+f[B][v],min(
	f[u][v]+f[A][B],f[u][B]+f[v][A]));
}
ll twice(int u,int v)
{
	return sum-f[A][B];
}
void dfs1(int u,int fa)
{
	vis[u]=1;
	for(auto v:g1[u])
	{
		if(vis[v] && v!=fa && u<v)
			ea[++k]=u,eb[k]=v;
		if(!vis[v])
		{
			g[u].push_back(v);
			dfs1(v,u);
		}
	}
}
void dfs2(int u,int fa)
{
	for(int v:g[u]) if(v^fa) dfs2(v,u);
	if(u==1) return ;
	if(b[u]) b[u]^=1,b[fa]^=1,res+=once(u,fa);
	else res+=twice(u,fa);
}
signed main()
{
	n=read();m=read();p=read();q=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		g1[u].push_back(v);
		g1[v].push_back(u);
	}
	for(int i=1;i<=p;i++)
		for(int j=1;j<=p;j++)
			f[i][j]=(i!=j)*inf;
	for(int i=1;i<=q;i++)
	{
		int u=read(),v=read(),c=read();sum+=c;
		f[u][v]=f[v][u]=c;d[u]++;d[v]++;
	}
	for(int k=1;k<=p;k++)
		for(int i=1;i<=p;i++)
			for(int j=1;j<=p;j++)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	for(int i=1;i<=p;i++)
	{
		if(d[i]%2 && !A) A=i;
		else if(d[i]%2) B=i;
	}
	if(!A) {printf("%lld
",m*sum);return 0;}
	dfs1(1,0);
	for(int s=0;s<(1<<k);s++)
	{
		for(int i=1;i<=n;i++) b[i]=0;res=0;
		for(int i=1;i<=k;i++)
			if(s&(1<<i-1))
			{
				b[ea[i]]^=1,b[eb[i]]^=1;
				res+=once(ea[i],eb[i]);
			}
			else res+=twice(ea[i],eb[i]);
		dfs2(1,0);
		ans=max(ans,res);
	}
	printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/C202044zxy/p/15525970.html