Living-Dream 系列笔记 第42期
T1
枚举流量对于花费跑 dijkstra 并取比值的 \(\max\) 即可。
关于为什么枚举流量不一定当前最优的问题,因为最优解的流量总在枚举范围内,所以无需考虑当前是否最优。
#include<bits/stdc++.h> using namespace std; int n,m,ans; int dis[100031]; bool vis[100031]; struct Edge{ int v,c,f; }; vector<Edge> G[200031]; struct Node{ int u,c,f; bool operator < (const Node &b) const{ return c>b.c; } }; void dijkstra(int s,int x){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)),dis[s]=0; priority_queue<Node> pq; pq.push({s,0}); while(!pq.empty()){ Node now=pq.top(); pq.pop(); if(vis[now.u]) continue; vis[now.u]=1; for(auto i:G[now.u]){ if(i.f>=x&&dis[i.v]>dis[now.u]+i.c) dis[i.v]=dis[now.u]+i.c, pq.push({i.v,dis[i.v]}); } } } int main(){ cin>>n>>m; for(int i=1,u,v,c,f;i<=m;i++) cin>>u>>v>>c>>f,G[u].push_back({v,c,f}),G[v].push_back({u,c,f}); int l=1,r=1000; while(l<=r){ int mid=(l+r)>>1; dijkstra(1,mid); if(dis[n]!=0x3f3f3f3f) l=mid+1,ans=1000000*mid/dis[n]; else r=mid-1; } cout<<ans; return 0; }
T2
考虑 dp,在松弛操作时进行转移。
当 dijkstra 发现一条新的最短路点时,直接继承即可。
否则如果发现邻接点 \(dis\) 值与当前最优值相等,加上当前点的 \(dp\) 值即可。
#include<bits/stdc++.h> using namespace std; const int MOD=100003,N=1000031; int n,m,ans; int dis[N],cnt[N]; bool vis[N]; vector<int> G[N<<1]; struct Node{ int u,w; bool operator < (const Node &b) const{ return w>b.w; } }; void dijkstra(){ memset(dis,0x3f,sizeof(dis)); cnt[1]=1,dis[1]=0; priority_queue<Node> pq; pq.push({1,0}); while(!pq.empty()){ Node now=pq.top(); pq.pop(); if(vis[now.u]) continue; vis[now.u]=1; for(auto i:G[now.u]){ if(dis[i]>dis[now.u]+1) dis[i]=dis[now.u]+1, cnt[i]=cnt[now.u], pq.push({i,dis[i]}); else if(dis[i]==dis[now.u]+1) cnt[i]=(cnt[i]+cnt[now.u])%MOD; } } } int main(){ cin>>n>>m; for(int i=1,u,v;i<=m;i++) cin>>u>>v,G[u].push_back(v),G[v].push_back(u); dijkstra(); for(int i=1;i<=n;i++) cout<<cnt[i]%MOD<<'\n'; return 0; }
T3
题意可转化为求免费完 \(k\) 对之后最大边权的最小值。
一眼二分。
考虑在 check
中重建图,令原边权 \(>mid\) 的边边权为 \(1\),\(<mid\) 的边边权为 \(0\)。
然后跑一边 dijkstra 求出需要免费的点对,
若数量 \(\le k\),则猜小,否则猜大即可。
#include<bits/stdc++.h> using namespace std; const int N=1031,M=10031; int n,m,k,ans; int dis[N]; bool vis[N]; struct P{ int a,b,l; }p[M]; struct E{ int v,w; }; vector<E> G[M]; struct Node{ int u,w; bool operator < (const Node &b) const{ return w>b.w; } }; void dijkstra(){ memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); dis[1]=0; priority_queue<Node> pq; pq.push({1,0}); while(!pq.empty()){ Node now=pq.top(); pq.pop(); if(vis[now.u]) continue; vis[now.u]=1; for(auto i:G[now.u]){ if(dis[i.v]>dis[now.u]+i.w) dis[i.v]=dis[now.u]+i.w, pq.push({i.v,dis[i.v]}); } } } bool check(int x){ for(int i=1;i<=n;i++) G[i].clear(); for(int i=1;i<=m;i++) G[p[i].a].push_back({p[i].b,(p[i].l>x)}), G[p[i].b].push_back({p[i].a,(p[i].l>x)}); dijkstra(); return dis[n]<=k; } int main(){ cin>>n>>m>>k; for(int i=1;i<=m;i++) cin>>p[i].a>>p[i].b>>p[i].l; int l=0,r=1e6+1; while(l+1<r){ int mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid; } if(r==1e6+1) cout<<-1; else cout<<r; return 0; }
作业 T1
我们跑两遍 dijkstra,求得 \(1,n\) 到其他点的最短路,记为 \(x_i,y_i\)。
于是答案即为 \(\min(T,\max\{x_i+y_j+1\})\)。
直接算是 \(O(n \log n+n^2)\) 的,T 飞。
于是对于升序的 \(x_i\),下面有两个引理:
- 引理 \(1\):对于每个 \(x_i\),仅需考虑其右边的 \(y_i\)。
证明:
若 \(j<i\),则有 \(x_i+y_j \ge x_j+y_j \ge T\),其不影响答案。
证毕
- 引理 \(2\):对于每个 \(x_i\),仅需考虑 \(y_{i+1}\)。
证明:
若 \(y_{i+1} \ge y_i\),则有 \(x_i+y_{i+1} \ge x_i+y_i \ge T\),说明考虑 \(j \ge i+1\) 的情况均对答案无影响。
否则说明 \(y_i\) 降序,则最大值一定出现在 \(y_{i+1}\)。
证毕
综上,答案即为 \(\min(T,\max\{x_i+y_{i+1}+1\})\)。
时间复杂度 \(O(n \log n + n)\)
#include<bits/stdc++.h> using namespace std; const int N=2000031; int n,m,k,ans,p[N]; int dis1[N],disn[N]; bool vis[N]; vector<int> G[N<<1]; struct Node{ int u,w; bool operator < (const Node &b) const{ return w>b.w; } }; bool cmp(int x,int y){ return dis1[x]<dis1[y]; } void dijkstra_1(){ memset(vis,0,sizeof(vis)); memset(dis1,0x3f,sizeof(dis1)),dis1[1]=0; priority_queue<Node> pq; pq.push({1,0}); while(!pq.empty()){ Node now=pq.top(); pq.pop(); if(vis[now.u]) continue; vis[now.u]=1; for(auto i:G[now.u]){ if(dis1[i]>dis1[now.u]+1) dis1[i]=dis1[now.u]+1, pq.push({i,dis1[i]}); } } } void dijkstra_n(){ memset(vis,0,sizeof(vis)); memset(disn,0x3f,sizeof(disn)),disn[n]=0; priority_queue<Node> pq; pq.push({n,0}); while(!pq.empty()){ Node now=pq.top(); pq.pop(); if(vis[now.u]) continue; vis[now.u]=1; for(auto i:G[now.u]){ if(disn[i]>disn[now.u]+1) disn[i]=disn[now.u]+1, pq.push({i,disn[i]}); } } } int main(){ cin>>n>>m>>k; for(int i=1;i<=k;i++) cin>>p[i]; for(int i=1,u,v;i<=m;i++) cin>>u>>v,G[u].push_back(v),G[v].push_back(u); dijkstra_1(),dijkstra_n(),sort(p+1,p+k+1,cmp); for(int i=1;i<k;i++) ans=max(ans,dis1[p[i]]+disn[p[i+1]]+1); ans=min(ans,dis1[n]),cout<<ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)