[ABC232G] Modulo Shortest Path
Problem Statement
We have a directed graph with vertices, called Vertex , Vertex , , Vertex .
For each pair of integers such that and , there is a directed edge from Vertex to Vertex of weight . (Here, denotes the remainder when is divided by .)
There is no edge other than the above.
Print the shortest distance from Vertex to Vertex , that is, the minimum possible total weight of the edges in a path from Vertex to Vertex .
Constraints
- All values in input are integers.
Input
Input is given from Standard Input in the following format:
$N$ $M$ $A_1$ $A_2$ $\ldots$ $A_N$ $B_1$ $B_2$ $\ldots$ $B_N$
Output
Print the minimum possible total weight of the edges in a path from Vertex to Vertex .
Sample Input 1
4 12 10 11 6 0 8 7 4 1
Sample Output 1
3
Below, denotes the directed edge from Vertex to Vertex .
Let us consider the path .
- Edge weighs ,
- Edge weighs ,
- Edge weighs .
Thus, the total weight of the edges in this path is .
This is the minimum possible sum for a path from Vertex to Vertex .
Sample Input 2
10 1000 785 934 671 520 794 168 586 667 411 332 363 763 40 425 524 311 139 875 548 198
Sample Output 2
462
一道优化建图题.
观察到这里的取模最多只会减一次,所以与其说是取模,不如说当 时,代价减去 。
先看如果没有取模,怎么做。一个经典的拆点,把一个点拆成内点和外点,外点向内点连代价为 的边,内点向外点连 的边。然后随便用0边把外点连起来。
但这样很明显不好扩展。注意到一件事,如果 ,将点按照 排序后, 的点的代价都要减去 。所以将所有点按照 排序,然后仍然拆成内点和外点,外点从点 连向 ,代价 。内外点之间连 0 边。这个构图非常巧妙,如果从点 的内点向某一个点连了一条边权为 的边,这个内点到达任何一个 ,都相当于有一条边权为 的边。对于一个内点,他先朝 的外点连一条边权为 的边,在二分出第一个 的点,连一条边权为 的边,就可以达到题目中的效果。
但这样好像有些点同时连了 和 的边?但其实不影响答案。因为题目求最短路。
所有的边权为正,可以跑dij.
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=4e5+5;
int n,b[N],e_num,m,u,v,hd[N],vis[N];
LL dis[N];
struct node{
int a,b,id;
bool operator<(const node&n)const{
return b<n.b;
}
}a[N];
struct edge{
int v,nxt,w;
}e[N<<3];
struct dian{
int v;
LL w;
bool operator<(const dian&d)const{
return w>d.w;
}
};
priority_queue<dian>q;
void add_edge(int u,int v,int w)
{
// printf("%d %d %d\n",u,v,w);
e[++e_num]=(edge){v,hd[u],w};
hd[u]=e_num;
}
void dijkstra(int s)
{
q.push((dian){s,0});
memset(dis,0x7f,sizeof(dis));
dis[s]=0;
while(!q.empty())
{
int k=q.top().v;
q.pop();
if(vis[k])
continue;
vis[k]=1;
for(int i=hd[k];i;i=e[i].nxt)
{
if(dis[e[i].v]>dis[k]+e[i].w)
{
dis[e[i].v]=dis[k]+e[i].w;
q.push((dian){e[i].v,dis[e[i].v]});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].a);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].b),a[i].id=i;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
b[i]=a[i].b;
for(int i=1;i<=n;i++)
{
// b[i]=a[i].b;
if(a[i].id==1)
u=i;
else if(a[i].id==n)
v=i;
add_edge(i+n,i,0);
add_edge(i,n+1,b[1]+a[i].a);
int k=lower_bound(b+1,b+n+1,m-a[i].a)-b;
// printf("%d %d\n",k,a[i].a);
if(k<=n)
add_edge(i,k+n,a[i].a+b[k]-m);
}
// printf("%d %d\n",u,v);
for(int i=1;i<n;i++)
add_edge(i+n,i+n+1,b[i+1]-b[i]);
dijkstra(u);
// for(int i=1;i<=n+n;i++)
// printf("%lld ",dis[i]);
printf("%lld",dis[v]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】