AT_abc319_g [ABC319G] Counting Shortest Paths
最短路、动态规划
好题,场上写的做法 TLE 了五个点,所以写一篇题解。
我是不是已经退役一个月了。
2023/9/11:更正了由 AcO2d 指出的语义混淆。
题意
在有
若节点
否则输出节点
分析
以下所有讨论的对象均为原图。
建出原图的时间复杂度为
初始时将起点入队,在未被遍历的点的集合中删除起点。
遍历未被遍历的点的集合并查询点
由于指定删除的边数量至多
由于 unordered_set 的情况下,这一部分的时间复杂度为
既然最短路长度已经求得,是不是可以连边后拓扑排序计算其数量了?否。即使边数有所减少,连边的代价仍然是不可接受的。
令
原图中几乎所有符合以上条件的点对之间存在贡献。
对于
总体时间复杂度
#include<bits/stdc++.h>
using namespace std;
constexpr int mod=998244353;
vector<int> e[200005],v[200005];
int n,m,dp[200005],dis[200005];
unordered_set<int> s,w[200005];
queue<int> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) s.emplace(i);
for(int i=1;i<=m;i++){
static int x,y;
scanf("%d%d",&x,&y);
w[x].emplace(y);//bfs时查询连边用
w[y].emplace(x);
v[x].push_back(y);//计算最短路数量用
v[y].push_back(x);
}
dp[1]=1,dis[1]=1,s.erase(1);
for(q.push(1);!q.empty();q.pop()){
vector<int> trash;
int now=q.front();
for(int i:s) if(!w[now].count(i)){
dis[i]=dis[now]+1;
q.push(i);
trash.push_back(i);
}
for(int i:trash) s.erase(i);
}
if(dis[n]==0) return puts("-1"),0;
for(int i=1;i<=n;i++) e[dis[i]].push_back(i);
for(int i=2;i<=dis[n];i++){
long long sum=0ll;
for(int j:e[i-1]) sum+=dp[j];//完全图在任意点之间都有连边
for(int j:e[i]){//枚举dis[j]=i的点
long long ans=sum;
for(int k:v[j]) if(dis[k]==dis[j]-1) ans-=dp[k];//减去没有连边的点的贡献
dp[j]=(ans%mod+mod)%mod;
}
}
printf("%d\n",dp[n]);
return 0;
}