JZOJ5895【NOIP2018模拟10.5】旅游
题目
Description
Input
Output
Sample Input
6 10
4 6
4 5
3 6
5 2
3 2
1 2
3 4
6 1
2 4
1 3Sample Output
2132
Data Constraint
题目大意
给你一个无向图,让你从起点出发,经过所以边至少一次的最小时间。
思考过程
这个东西值得深思。
可以发现两点之间的最短路径的长度大于最短路径上的最长边,小于它的两倍。
因为最短路径中一个边不可能经过两次,对于最长边,任意
这是很显然的。
很容易想出这题或许和最小生成树有关系。
如果要求两点间的最短路,就要使路径上的最长边最小。
因为最小生成树后,对于两个点和,它们的最短路径路径一定在树上。
如果有一条非树边,的长度必定小于它们在树上的路径长度。
所以每一条非树边只会经过一次。
然后呢,然后呢,我就想不出什么性质了……
正解
要用到一个叫欧拉回路的东西。
无向图中,如果每个点的度数为偶数,那么就是欧拉回路(可以理解成能够一笔画的图),否则不是。
在这题中,每个点不一定都是奇数度数。
所以我们要将那些奇数度数的点两两配对。
配对后给它们之间加一条边,这条边的长度即是最小生成树上两点之间的路径长度。
然后整个图就变成了一个欧拉回路了……
那么如何两两配对?
其实我们并不需要真正地两两配对。
对于树上的一个点,如果它的子树的奇数度点的个数为奇数时,说明在那些点中肯定有一个点,必定是和子树外面的点配对,所以就必须经过和它的父亲之间的那条边。
所以直接记录下贡献就好了。
还有,每条边最多经过两次……
怎么证明呢?感性理解吧,我不会。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 500000
#define MAXM 500000
#define mod 998244353
int n,m;
int pow2[MAXM+1];
struct edge{
int u,v;
} ed[MAXM+1];
int fa[MAXN+1];
int getfa(int x){
if (fa[x]==x)
return x;
return fa[x]=getfa(fa[x]);
}
struct EDGE{
int to,num;
EDGE *las;
} e[MAXM*2+1];
int ne;
EDGE *lst[MAXN+1];
inline void link(int u,int v,int num){
++ne;
e[ne]={v,num,lst[u]};
lst[u]=e+ne;
}
int d[MAXN+1];
int sum[MAXN+1];
void dfs(int,int);
int ans=0;
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d%d",&n,&m);
pow2[0]=1;
for (int i=1;i<=m;++i){
pow2[i]=(pow2[i-1]<<1)%mod;
ans=(ans+pow2[i])%mod;//每个点至少经过一次
}
for (int i=1;i<=m;++i){
scanf("%d%d",&ed[i].u,&ed[i].v);
d[ed[i].u]++;
d[ed[i].v]++;
}
//最小生成树
for (int i=1;i<=n;++i)
fa[i]=i;
for (int i=1;i<=m;++i){
int x=getfa(ed[i].u),y=getfa(ed[i].v);
if (x!=y){
fa[x]=y;
link(ed[i].u,ed[i].v,i),link(ed[i].v,ed[i].u,i);
}
}
dfs(1,0);
printf("%d\n",ans);
return 0;
}
void dfs(int x,int fa){
sum[x]=d[x];
for (EDGE *ei=lst[x];ei;ei=ei->las)
if (ei->to!=fa){
dfs(ei->to,x);
sum[x]^=sum[ei->to];//其实真正有价值的是二进制的第0位,直接异或起来只是方便了
if (sum[ei->to]&1)
ans=(ans+pow2[ei->num])%mod;//统计答案
}
}
总结
欧拉回路……