【题解】洛谷 P4061 [Code+#1]大吉大利,晚上吃鸡!
前言
巨佬们的题解都好难懂(我是仔细阅读了 @Cyhlnj 题解中的每一句代码才弄懂的),于是我这个蒟蒻打算写一篇思路清晰、尽可能严谨的题解,如有错误还请指正。
顺带一提,这里有两组 Hack 数据。截止本文发布前(2021.8.25),本题题解区只有 FrenkiedeJong21 的题解能通过这两组数据(当然本题解的代码是可以过的),具体原因见这个帖子。
题意简述
给定
条件:对于任意一条从
数据范围:
P.S. 本题数据可能出现从
P.S. 为了方便叙述,默认下文中的“路径”“最短路径”等均指“从
分析
设
容易想到一个满足题意的必要条件:
进一步,答案应为:满足
由于
题解
首先任取一条
结论一
对于固定的
结论一证明
设
那么有两种情况:
- 存在一条最短路径
P_1 ,先经过Y 再经过B 。- 将路径
P_1 从S 到Y 的部分用路径P 从S 到Y 的部分替换,得到路径P_2 。显然P_2 也为最短路径,且P_2 先经过X 再经过B ,这与X 和B 满足特殊性质矛盾。
- 将路径
- 存在一条最短路径
P_3 ,先经过B 再经过Y 。- 将路径
P_3 从Y 到T 的部分用路径P 从Y 到T 的部分替换,得到路径P_4 。显然P_4 也为最短路径,且P_4 先经过B 再经过Z ,这与Z 和B 满足特殊性质矛盾。
- 将路径
因此假设不成立,则结论一成立。
结论二
设
结论二证明
设
假设存在一条先经过
若
若
这与
故结论二成立。
推论
由结论二可知:把所有最短路径看成有向路径,然后取它们的并集,所形成的有向图是一个 DAG(Directed Acyclic Graph,有向无环图)。
求解不存在路径同时经过 x 和 y 的无序点对 (x,y)
将路径
由结论一,设路径
其中条件
为了便于计算,我们将其改成递推形式:
其中条件
由推论可知,由于有向最短路径的并集形成的图是 DAG,可以使用拓扑排序对
求解其中满足 F 之和等于 F(T) 的无序点对数量
由上一部分知,所有的、不存在路径同时经过
至此,只需解决最后一个问题:对于每个
拿一个 map 当桶,在计算
注意:在本题的限制下,起点
时间复杂度分析
堆优化 Dijkstra 是
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,S,T;
const int max_n=5e4+5;
const int max_m=5e4+5;
int End[max_m<<1],Last[max_n],Next[max_m<<1],Len[max_m<<1],e;
inline void add_edge(int x,int y,int z)
{
End[++e]=y,Next[e]=Last[x],Last[x]=e,Len[e]=z;
End[++e]=x,Next[e]=Last[y],Last[y]=e,Len[e]=z;
}
typedef pair<long long,int> P;
priority_queue<P,vector<P>,greater<P> > Q;
long long dis[2][max_n];
const int mod=1e9+7;
inline void add(int &a,int b)
{
a=a+b-(a+b>=mod?mod:0);
}
inline int get_dif(int a,int b)
{
return a-b+(a<b?mod:0);
}
int f[2][max_n],pre[max_n];
inline void Dijkstra(int op)
{
for(int i=1;i<=n;++i)
dis[op][i]=1e18;
if(!op)
{
dis[op][S]=0,f[op][S]=1;
Q.push(P(0,S));
}
else
{
dis[op][T]=0,f[op][T]=1;
Q.push(P(0,T));
}
while(Q.size())
{
long long d=Q.top().first;
int x=Q.top().second;
Q.pop();
if(dis[op][x]<d)
continue;
for(int i=Last[x];i;i=Next[i])
{
int y=End[i];
if(d+Len[i]<dis[op][y])
{
dis[op][y]=d+Len[i];
f[op][y]=f[op][x];
if(op)
pre[y]=x;
Q.push(P(dis[op][y],y));
}
else if(d+Len[i]==dis[op][y])
add(f[op][y],f[op][x]);
}
}
}
inline bool check(int op,int x,int y,int w)
{
return dis[op][x]+w+dis[op^1][y]==dis[0][T];
}
int p[max_n],tot,l[max_n],r[max_n],d[max_n],que[max_n],head,tail;
inline void TopSort(int op)
{
for(int x=1;x<=n;++x)
for(int i=Last[x];i;i=Next[i])
{
int y=End[i];
if(check(op,x,y,Len[i]))
++d[y];
}
head=1,tail=0;
for(int i=1;i<=n;++i)
{
if(!d[i])
que[++tail]=i;
}
while(head<=tail)
{
int x=que[head++];
for(int i=Last[x];i;i=Next[i])
{
int y=End[i];
if(check(op,x,y,Len[i]))
{
op?r[y]=min(r[x],r[y]):l[y]=max(l[x],l[y]);
if(!--d[y])
que[++tail]=y;
}
}
}
assert(tail==n);
}
int F[max_n];
vector<int> id_l[max_n],id_r[max_n];
map<int,int> cnt;
int main()
{
scanf("%d%d%d%d",&n,&m,&S,&T);
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
}
Dijkstra(0);
if(dis[0][T]==1e18)
{
printf("%lld\n",n*(n-1ll)>>1);
return 0;
}
Dijkstra(1);
for(int i=S;i;i=pre[i])
p[++tot]=i,l[i]=tot+1,r[i]=tot-1;
for(int i=1;i<=n;++i)
{
if(dis[0][i]+dis[1][i]==dis[0][T])
F[i]=1ll*f[0][i]*f[1][i]%mod;
if(!l[i])
l[i]=1,r[i]=tot;
}
TopSort(0),TopSort(1);
for(int i=1;i<=n;++i)
{
if(l[i]<=r[i])
{
id_l[l[i]].push_back(i);
id_r[r[i]].push_back(i);
}
}
long long ans=0;
for(int i=1;i<=tot;++i)
{
for(vector<int>::iterator it=id_l[i].begin();it!=id_l[i].end();++it)
++cnt[F[*it]];
ans+=cnt[get_dif(F[T],F[p[i]])];
for(vector<int>::iterator it=id_r[i].begin();it!=id_r[i].end();++it)
--cnt[F[*it]];
}
printf("%lld\n",ans);
return 0;
}