不需要跑次短路,用通法来解决这个题。
通法题:逛公园
(而且这题没有毒瘤的0环,容易多了)
先跑一遍最短路把到每个点的最短距离$dis[u]$求出来。
然后dp:设当前考虑的点是u,到这个点的距离是$dis[u]+k$,$f[k][u]$表示到$u$,长度为$dis[u]+k$的路径数量
它会对$dis[v]=dis[u]+w(u,v)$的$v$产生$f[k][v]+=f[k][u]$的贡献;并且如果$k=0,dis[v]+1=dis[u]+w(u,v)$还会产生$f[1][v]+=f[0][u]$的贡献
为了消除后效性,需要按照$dis[u]$排序再dp,最后的答案就是$f[0][t]+f[1][t]$
时间复杂度瓶颈在于最短路,为$O(mlogn)$
/**********/
#define MAXN 2011
#define MAXM 20011
struct Edge
{
ll v,w,nxt;
}e[MAXM];
ll cnt=0,last[MAXN];
void adde(ll u,ll v,ll w)
{
++cnt;
e[cnt].v=v;e[cnt].w=w;
e[cnt].nxt=last[u],last[u]=cnt;
}
ll dis[MAXN];
struct node
{
ll u,dis;
node(ll _u=0,ll _dis=0)
{
u=_u,dis=_dis;
}
bool operator <(const node& t)
const
{
return dis>t.dis;
}
};
std::priority_queue<node>q;
void Dij(ll s)
{
memset(dis,0x3f,sizeof dis);
dis[s]=0;
q.push(node(s,0));
while(!q.empty())
{
ll u=q.top().u,tmp=q.top().dis;q.pop();
if(dis[u]!=tmp)continue;
for(ll i=last[u];i;i=e[i].nxt)
{
ll v=e[i].v;
if(umin(dis[v],dis[u]+e[i].w))q.push(node(v,dis[v]));
}
}
}
ll f[2][MAXN],ord[MAXN];
bool cmp(ll x,ll y)
{
return dis[x]<dis[y];
}
void work()
{
cnt=0;memset(last,0,sizeof last);
memset(f,0,sizeof f);
ll n=read(),m=read();
for(ll i=1;i<=m;++i)
{
ll u=read(),v=read(),w=read();
adde(u,v,w);
}
ll s=read(),t=read();
Dij(s);
for(ll i=1;i<=n;++i)ord[i]=i;
std::sort(ord+1,ord+n+1,cmp);
f[0][s]=1;
for(ll k=0;k<=1;++k)
for(ll p=1;p<=n;++p)
{
ll u=ord[p];
//printf("dis[%lld]=%lld\n",u,dis[u]);
for(ll i=last[u];i;i=e[i].nxt)
{
ll v=e[i].v;
if(dis[u]+e[i].w==dis[v])f[k][v]+=f[k][u];
if(k==0&&dis[u]+e[i].w==dis[v]+1)f[1][v]+=f[0][u];
}
}
printf("%lld\n",f[0][t]+f[1][t]);
}
int main()
{
ll task=read();
while(task--)work();
return 0;
}
%%%感谢佬
超时了吗?
or2
想问一下各位佬,可以先用djkstra求出每个点的dist,然后用bfs来更新f[j][0]和f[j][1]吗
也可以的,但毕竟for一下比bfs省力点。
为啥k=0和k=1不能分开算
也行吧,就是麻烦点
等一篇注释请问一下,洛谷那题如果排序dp的话0环怎么处理呀
instack
0环有一点麻烦,一种方法是建一个只有0边的图,然后缩点,一个SCC里面的点距离都一样,然后就可以用这个题的方法。
是不是正是由于 “ 如果k=0,dis[v]+1=dis[u]+w(u,v)还会产生f[1][v]+=f[0][u]的贡献 ” ,导致dis相等的两个点可能互相更新,从而使 f 数组无法在求最短路时在线求?
我们首先用 x 更新了与其相连的点的 f,这时一个 y 点(dis[x] == dis[y])又可能更新 x 的 f ,如果我们再次利用 x 点被 y 更新后的 f 更新与之相连的点的 f,就会导致这些点的 f 被重复计算。
刚开始我就是在线计算的 f,结果一直过不去......
是的。
有个问题想了一晚上也没能想通,就是为什么k=0和k=1的情况要分开更新,先更新k=0然后更新k=1
两个一起更新为啥不行,按照dis从低到高排序后,照理说后面不是不会影响到前面。。。
但是我一起更新的时候(用的如下代码):
就wa了
改成题解中的先计算k=0然后计算k=1的时候就过了。
dis单调不降,当$dis_j=dis_k(j<k)$的时候
f[j][1]
可能不对哦哦,听您这么一说,我发现确实会有这种情况,感激不尽
不客气
萌新表示这种做法可以理解但是做题时很难想到啊!考场上更别说了…
多做做就会了.加油.(说实话我当时做这题也不是完全独立思考的,不过现在看来就很显然了)
加油共勉!
我也是卡了3个点,原来是这个原因
我也好不容易想明白了,附一组数据
100
4 5
1 2 2
1 3 2
2 3 1
3 2 1
2 4 1
1 4
一起算的话 算2到4时因为2 3同长3在后,没有计算把3的最短加到2的次短里,延误了,此时2的次短还没到达正确值,就用来计算4的次短继承了,所以4的次短路个数是错的
我觉得 反向跑一遍spfa (为了给 dfs 剪枝),再 dfs 一遍就好,代码也不长
大佬好,咨询一下,这个用的是什么算法了?
似乎是Dijkstra堆优化算法
为啥dist数组从小到大排序就是括扑序啊orz
因为没有负权边,那么对于$dis(x)<dis(y)$,$y$就无法对$x$产生贡献.但$x$可能对$y$有贡献.令$y$在$x$之后即可消除后效性
我傻了,这里是dijkstra,和洛谷混了233,谢谢巨巨