P6628-[省選聯考 2020 B 卷] 丁香之路【歐拉回路,最小生成樹】
阿新 • • 發佈:2021-08-23
正題
題目連結:https://www.luogu.com.cn/problem/P6628
題目大意
給出\(n\)個點的一張完全無向圖,\(i\sim j\)的邊權是\(|i-j|\)。
然後給出\(m\)條必經邊,和起點\(s\)。
求對於每個終點經過所有必經邊的最短路徑。
\(1\leq n\leq 2500,0\leq m\leq \frac{n(n-1)}{2}\)
解題思路
很經典的模型,首先起點和終點連一條邊,然後考慮加最少的邊使得有歐拉回路。
歐拉回路有兩個條件,度數都是偶數很好滿足,直接把相鄰的奇點連邊肯定最優,但是還需要滿足連通的條件。
考慮到圖上邊權的特殊性,我們顯然只需要使用形如\(i\sim i+1\)
時間複雜度\(O(m+n^2\log n)\)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=2510; struct edge{ int x,y,w; }e[N]; int n,m,s,cnt,ans,k,B[N*N]; int deg[N],fa[N],pf[N],b[N<<1]; int find(int x) {return (fa[x]==x)?x:(fa[x]=find(fa[x]));} void unionn(int x,int y){ x=find(x);y=find(y); if(x!=y)fa[x]=y; return; } bool cmp(edge x,edge y) {return x.w<y.w;} int main() { scanf("%d%d%d",&n,&m,&s); int sum=0; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); unionn(x,y);deg[x]++;deg[y]++; B[++cnt]=x;B[++cnt]=y;sum+=abs(x-y); } B[++cnt]=s;sort(B+1,B+1+cnt); cnt=unique(B+1,B+1+cnt)-B-1; for(int i=1;i<=n;i++)pf[i]=find(i); deg[s]++;m=0; for(int t=1;t<=n;t++){ deg[t]++;ans=sum;int last=0; for(int i=1;i<=cnt;i++)b[i]=B[i]; k=cnt;b[++k]=t; sort(b+1,b+1+k); k=unique(b+1,b+1+k)-b-1; for(int i=1;i<=n;i++)fa[i]=pf[i]; for(int i=1;i<=n;i++) if(deg[i]&1){ if(last){ for(int j=last;j<i;j++)unionn(i,j); ans+=i-last;last=0; } else last=i; } for(int i=1;i<k;i++) e[i]=(edge){b[i],b[i+1],b[i+1]-b[i]}; sort(e+1,e+k,cmp); for(int i=1;i<k;i++){ int x=find(e[i].x),y=find(e[i].y); if(x==y)continue; fa[x]=y;ans+=e[i].w*2; } printf("%d ",ans);deg[t]--; } return 0; }