本文主要是介绍NOIP2023模拟11联测32 百日草,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题解
有一个有 n n n个点 m m m条边的有向图,每条边上有一个正整数边权,你需要顺着图上的有向边从 1 1 1号点走到 n n n号点。
假设你经过的边边权依次为 w 1 , w 2 , … , w t w_1,w_2,\dots,w_t w1,w2,…,wt,则你的疲惫程度为 max i = 1 t i ⋅ w i \max\limits_{i=1}^ti\cdot w_i i=1maxti⋅wi,你需要找到最小疲惫程度的路径。
2 ≤ n , m ≤ 3 × 1 0 5 , 1 ≤ w i ≤ 1 0 9 2\leq n,m\leq 3\times 10^5,1\leq w_i\leq 10^9 2≤n,m≤3×105,1≤wi≤109
题解
我们可以二分答案 a n s ans ans。设 d i s i dis_i disi表示从 1 1 1号点到 i i i号点要经过的最少边数(注意如果要从 u u u经过边 i i i到达 v v v,则要满足 ( d i s u + 1 ) × w i ≤ a n s (dis_u+1)\times w_i\leq ans (disu+1)×wi≤ans),那么用 dijkstra \text{dijkstra} dijkstra即可求出所有能够从 1 1 1号点到达的点的 d i s dis dis值,再判断能否到达 n n n即可。
这样做时间复杂度为 O ( n log n log v ) O(n\log n\log v) O(nlognlogv),其中 v v v为 a n s ans ans的值域,也就是 m max w i m\max w_i mmaxwi。下面考虑优化。
我们发现如果点 y y y是从点 x x x转移过来的,则 d i s y = d i s x + 1 dis_y=dis_x+1 disy=disx+1,于是我们可以将 dijkstra \text{dijkstra} dijkstra改为 01 BFS 01\text{BFS} 01BFS,那么时间复杂度就降为 O ( n log v ) O(n\log v) O(nlogv),就可以过了。
code
#include<bits/stdc++.h>
using namespace std;
const int N=300000;
int n,m,tot=0,d[N+5],l[N+5],r[N+5],z[N+5],dis[N+5];
long long w[N+5];
queue<int>q;
void add(int xx,int yy,int zz){l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;w[tot]=zz;
}
bool check(long long mid){for(int i=1;i<=n;i++){z[i]=0;dis[i]=1e9;}dis[1]=0;q.push(1);while(!q.empty()){int u=q.front();q.pop();if(u==n) return 1;for(int i=r[u];i;i=l[i]){if((dis[u]+1)*w[i]>mid) continue;if(dis[u]+1<dis[d[i]]){dis[d[i]]=dis[u]+1;if(!z[d[i]]){z[d[i]]=1;q.push(d[i]);}}}}return 0;
}
int main()
{
// freopen("zinnia.in","r",stdin);
// freopen("zinnia.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1,x,y,z;i<=m;i++){scanf("%d%d%d",&x,&y,&z);add(x,y,z);}long long l=1,r=3e14,mid;while(l<=r){mid=l+r>>1;if(check(mid)) r=mid-1;else l=mid+1;}printf("%lld",r+1);return 0;
}
这篇关于NOIP2023模拟11联测32 百日草的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!