[JZOJ 5895]【NOIP2018模擬10.5】旅遊
阿新 • • 發佈:2018-11-10
Description
給出一個n個點,m條邊的無向圖,保證無重邊自環,第i條邊的長度為
現在需要你找一條從1號點出發從1號點結束的迴路,保證每條邊至少經過一次,可以重複經過。求迴路長度的最小值。
Solution
考慮一個無向圖存在歐拉回路的條件:每個點的度數均為偶數
定義度數為偶數的點為偶點,相應的定義奇(ji,一聲)點
那麼我們將所有奇點提出來,要將它們變成偶點。
相當於我們要在這些奇點之間連邊,長度為它們之間的最短路,要連總長最少的邊使得所有奇點都變成偶點。
答案就是原本所有的邊權和+我們新連的邊
根據最短路的性質,若存在邊(x,y),(y,z),(y,p),那麼不如連(x,z),(y,p),因為dis(x,y)+dis(y,z)>=dis(x,z)(相當於列舉中轉點,聯想Floyd演算法)
這樣一來,我們連的邊必然是一個完美匹配(即兩兩匹配,每個點會且只會匹配另一個)
這麼一來好像變成了帶邊權完全圖的最小權最大匹配
好像是什麼帶花樹?好像還是N^3的?(逃)
這可是NOIP!
咦我們發現,邊權有問題
第i條邊的長度為2^i
也就是說,所有小的加起來都不如一個大的大
那麼路徑一定就在最小生成樹上了。。。
我們不妨先任意匹配這些奇點
此時我們考慮兩個匹配(x,y),(p,q)
如果它們路徑有重疊那肯定不優,因為一定可以重組變成沒有重疊
發現這和樹上路徑的異或很像。
任意匹配,將每一組奇點在最小生成樹上的路徑標記,如果一條邊被標記了奇數次則計入答案
然而有一種更機智的做法。
在每個奇點都打上1的標記,DFS最小生成樹,如果某個點i為根的子樹中標記的異或值為1則答案需要加上i到i父親這條邊的邊權
這與上面的過程是等價的
由於邊權是按順序2^i給出的,我們連排序都省了。
複雜度
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define LL long long
#define mo 998244353
#define N 500005
using namespace std;
int m1,n,m,rd[N],fs[N],pr[2*N],nt[2*N],dt[2*N],d[N],d1[N],bz[N],f[N],a1[N][2];
LL ans,cf[N];
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
pr[m1]=z;
}
int getf(int k)
{
if(!f[k]) return k;
return f[k]=getf(f[k]);
}
void dfs(int k,int fa,LL s)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa) dfs(p,k,pr[i]),bz[k]^=bz[p];
}
if(bz[k]) ans=(ans+s)%mo;
}
int main()
{
cin>>n>>m;
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
a1[i][0]=x,a1[i][1]=y;
rd[x]++,rd[y]++;
}
fo(i,1,n) if(rd[i]&1) bz[i]=1;
int j=0;
cf[0]=1;
ans=0;
fo(i,1,m) cf[i]=cf[i-1]*(LL)2%mo,ans=(ans+cf[i])%mo;
fo(i,1,m)
{
int fx=getf(a1[i][0]),fy=getf(a1[i][1]);
if(fx!=fy)
{
f[fx]=fy;
link(a1[i][0],a1[i][1],cf[i]);
link(a1[i][1],a1[i][0],cf[i]);
}
}
dfs(1,0,0);
printf("%lld\n",ans);
}