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 3
Sample Output
2132
Data Constraint
题解
看到这道题,我们想到了什么?
欧拉回路!
欧拉回路指在一个图中,从一个点开始是否可以经过每条边恰好一次最后回到起点。
然后,对于一个无向图,只要每一个点的度数为偶数则表示:从任意一个点出发都可以找到至少一种方案使得——经过每条边恰好一次最后回到起点。
所以说我们就要把题目给的图弄成欧拉回路。
怎么弄?
我们发现,对于每一个点的度数为偶数或奇数(废话),而且奇数的点的个数为偶数个(显然)。
所以说,我们根据某些神奇的东西,可以发现——
奇数点两两匹配后,在两个匹配的点之间找到一条最短的路,作为一条新的路,这样就消除了这两个点的度数为奇数的情况。
(新的路等价于重复走这一条最短的路。)
怎么匹配?怎么找最短路?
我们发现,对于两个点之间一条路的代价“”,我们找到它们的另一条路的代价“”,且p[i]<n.
那么就是说后者比前者更优。“”
那么我们就对于编号最小生成树即可。
这样对于两个点之间最短距离就为树上的距离,证明显然。
那说到底,怎么匹配?
我们发现对于一棵树,树上很多的点之间两两匹配。
最优方案中,任何两组匹配的路径不会重复走同一条路。
证明我不会
这样说来,每一条路径就有两种情况——
1、被经过一次,要加入答案。
2、不被经过,不加入答案。
那么我们每次指定一条边,如果它删除后两颗树上关键点的数量都为奇数,
那么代表这条边要被经过一次。
为什么?因为两颗树中两点两两匹配都剩一个点,这剩下的点就要经过这条边互相匹配了。
最后,答案再加上即可。
uses math;
const up=500000;
var
i,j,k,l,n,m,tot,gs,xx,yy:longint;
op,ans,answer:int64;
mo:int64=998244353;
x,y,p:array[1..up] of longint;
edge,f:array[1..up] of longint;
tov,next,last,val:array[1..2*up] of longint;
v,id,pre,fa,top,son,siz,dep:array[1..up] of longint;
jl:array[0..up] of longint;
tree:array[1..4*up] of longint;
flag,dp:array[1..up] of longint;
procedure insert(x,y,z:longint);
begin
inc(tot);
tov[tot]:=y;
next[tot]:=last[x];
last[x]:=tot;
val[tot]:=z;
end;
function getfather(x:longint):longint;
begin
if f[x]=x then exit(f[x]);
f[x]:=getfather(f[x]);
exit(f[x]);
end;
procedure dp1(v,fa:longint);
var
i,j,k,l:longint;
begin
dp[v]:=flag[v];
i:=last[v];
while i>0 do
begin
if tov[i]<>fa then
begin
dp1(tov[i],v);
dp[v]:=dp[v]+dp[tov[i]];
end;
i:=next[i];
end;
end;
procedure dp2(v,fa:longint);
var
i,j,k,l:longint;
begin
i:=last[v];
while i>0 do
begin
if tov[i]<>fa then
begin
if dp[tov[i]] mod 2=1 then
begin
answer:=answer+val[i];
end;
dp2(tov[i],v);
end;
i:=next[i];
end;
end;
begin
assign(input,'travel.in');reset(input);
assign(output,'travel.out');rewrite(output);
readln(n,m);
op:=1;
for i:=1 to m do
begin
readln(x[i],y[i]);
inc(edge[x[i]]);
inc(edge[y[i]]);
op:=(op*2) mod mo;
p[i]:=op;
end;
for i:=1 to n do f[i]:=i;
for i:=1 to m do
begin
xx:=getfather(x[i]);
yy:=getfather(y[i]);
if xx<>yy then
begin
f[xx]:=yy;
insert(x[i],y[i],p[i]);
insert(y[i],x[i],p[i]);
inc(k);
end;
if k=n-1 then break;
end;
j:=0;
op:=0;
for i:=1 to n do
begin
if edge[i] mod 2=1 then
begin
inc(j);
if op=0 then
begin
j:=1;
jl[j]:=i;
end
else
begin
inc(j);
jl[j]:=i;
end;
flag[jl[j]]:=1;
end;
end;
op:=j;
dp1(1,0);
dp2(1,0);
op:=1;
for i:=1 to m do
begin
op:=(op*2) mod mo;
answer:=(answer+op) mod mo;
end;
writeln(answer);
end.
我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。