标签:路径 需要 欧拉 its 图论 最小 最小生成树 abs mat
一张有 \(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