题意
给你一个无向图,若两点之间的最短路为奇数,那么这叫做不和谐最短路。若一条不和谐最短路上包含地点C,则称它为“经过C 的不和谐最短路”经过它的不同的不和谐最短路数量。两条最短路不同,当且仅当它们途径的地点的序列不同。求每个点所经过的不和谐最短路的不同的条数。(注意最短路的两端点也算)
数据范围
对于50% 的数据,N ≤ 100;
对于全部数据,N ≤ 1000;M ≤ 3000,每条路的长度不超过1000。
保证图连通,无自环重边。
分析
50%做法
其实这道题的50分是很好做的,可以用 Floyd 去实现,附czy代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int INF=6000000;
const int N=100;
long long sum[N+1][N+1];
int dist[N+1][N+1];
long long f[N+1];
int n,m;
void floyd()
{
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
if (i!=k)
for (int j=1;j<=n;j++)
if (j!=k&&i!=j)
if (dist[i][j]>dist[i][k]+dist[k][j])
{
dist[i][j]=dist[i][k]+dist[k][j];
sum[i][j]=sum[i][k]*sum[k][j];
}
else
if (dist[i][j]==dist[i][k]+dist[k][j])
sum[i][j]+=sum[i][k]*sum[k][j];
}
int main()
{
//freopen("san.in","r",stdin);
//freopen("san.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
dist[i][j]=INF;
dist[i][i]=0;
sum[i][i]=1;
}
for (int i=1,a,b,l;i<=m;i++)
{
scanf("%d %d %d",&a,&b,&l);
dist[a][b]=dist[b][a]=l;
sum[a][b]=sum[b][a]=1;
}
floyd();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
if (dist[j][i]+dist[i][k]==dist[j][k]&&dist[j][k]&1)
f[i]+=sum[j][i]*sum[i][k];
for (int i=1;i<=n;i++)
printf("%lld\n",f[i]);-
return 0;
}
很好理解,就是求两点的最短路的方案数,然后再把其经过的点的答案加上即可。
100%做法
其实我们可以发现这题求的是最短路中点的个数,那么是否不是最短路的边可以删除呢?答案是肯定的,我们可以先从每个点去做一次最短路树,那么这棵树里面的每条边都是有“价值”的。这时我们再去想 DP 方程,我们设从枚举的起点到第i个点的最短路的方案( g[i,0..1],0..1 表示奇或偶),再统计从第i个点到他下面的点的最短路的方案( f[i,0..1]0..1 表示奇或偶)那么第i个点可以加的答案便是 f[i,1]∗g[i,0]+f[i,0]∗g[j,1] 。(最后不要忘记了还有两端点的方案统计)
易错点
在方程转移时我们若不打一次拓扑排序那么我们将会把一些转移转移错,比如说a->b,c->b,b->d时,我们会发现统计f数组时,若我们先走a->b,b->d,c->b,那么我们便把错误的信息b传给d。所以这便是排拓扑序的原因了(之前我就因为没有排,导致我调试了很久还是没有发现错误。。。)
procedure dfs(x:longint);
var i:longint;
begin
i:=last[x];bd[x]:=true;
while i<>0 do begin
if not bd[d[i]] then
dfs(d[i]);i:=next[i];
end;
pe[pe[0]]:=x;dec(pe[0]);//拓扑排序
end;
代码:
var
bz,bd:array[1..1000] of boolean; oi:boolean;
dis,pe:array[0..1000] of longint;
an:array[1..1000] of int64;
nex,las,b,o,d,next,last,r,v,a:array[1..6000] of longint;
f:array[0..1,1..1000] of longint;
g:array[1..1000,0..1] of longint;
pre:array[1..1000,0..100] of longint;
num,nu,n,m,x,y,z,i,j,p,h,k:longint;
procedure insert(x,y,z:longint);
begin
inc(nu);
b[nu]:=y;
nex[nu]:=las[x];
las[x]:=nu;v[nu]:=z;o[nu]:=x;
end;
procedure add(x,y,z:longint);
begin
inc(num);
d[num]:=y;
next[num]:=last[x];
last[x]:=num;r[num]:=z;
end;
procedure spfa(x:longint);
var l,r,p,now,i:longint;
begin
fillchar(dis,sizeof(dis),$7f);
fillchar(bz,sizeof(bz),false);
l:=0;r:=1;dis[x]:=0;a[1]:=x;
while l<r do begin
inc(l);
now:=a[l];
p:=las[now];
while p<>0 do begin
if dis[b[p]]>dis[now]+v[p] then begin
dis[b[p]]:=dis[now]+v[p];
pre[b[p],0]:=0;
inc(pre[b[p],0]);pre[b[p],pre[b[p],0]]:=p;
if not bz[b[p]] then begin
bz[b[p]]:=true;inc(r);a[r]:=b[p];
end;
end else if (dis[b[p]]=dis[now]+v[p]) then begin
inc(pre[b[p],0]);pre[b[p],pre[b[p],0]]:=p;
end;
p:=nex[p];
end;
bz[now]:=false;
end;
end;
procedure dfs(x:longint);
var i:longint;
begin
i:=last[x];bd[x]:=true;
while i<>0 do begin
if not bd[d[i]] then
dfs(d[i]);i:=next[i];
end;
pe[pe[0]]:=x;dec(pe[0]);
end;
begin
readln(n,m);
for i:=1 to m do begin
readln(x,y,z);
insert(x,y,z);
insert(y,x,z);
end;
for i:=1 to n do begin
num:=0;
fillchar(last,sizeof(last),0);
fillchar(next,sizeof(next),0);
fillchar(f,sizeof(f),0);
fillchar(g,sizeof(g),0);
for j:=1 to n do f[0,j]:=1;
spfa(i);
for j:=1 to n do begin
for k:=1 to pre[j,0] do add(o[pre[j,k]],b[pre[j,k]],v[pre[j,k]]);
pre[j,0]:=0;
end;
fillchar(bd,sizeof(bd),false);pe[0]:=n;
dfs(i);
for j:=n downto 1 do begin
h:=pe[j];
p:=last[h];
while p<>0 do begin
inc(f[1,h],f[1-(r[p]mod 2),d[p]]);
inc(f[0,h],f[r[p]mod 2,d[p]]);p:=next[p];
end;
end;
g[i,0]:=1;
for j:=1 to n do begin
h:=pe[j];
p:=last[h];
while p<>0 do begin
inc(g[d[p],1],g[h,1-(r[p]mod 2)]);
inc(g[d[p],0],g[h,r[p] mod 2]);p:=next[p];
end;
if h<>i then begin
inc(an[h],f[1,h]*g[h,0]+f[0,h]*g[h,1]);
if dis[h] mod 2<>0 then inc(an[i],g[h,1]);
end;
end;
x:=x;
end;
for i:=1 to n do writeln(an[i]);
end.