题解:AT_abc232_g [ABC232G] Modulo Shortest Path
思路来自于 @0x3b800001 的(稍改),但是我希望我能用更清晰的语言让大家理解。
这篇比较偏向萌新。因为作者也是萌新呀!
暴力
直接暴力建边,然后跑一遍最短路即可。
正解
观察一条边的边权:
发现有取模操作,先考虑没有取模操作的特殊情况。
即:
我们该如何建边?我们可以枚举
此时,我们可以建立一个大节点
我们可以让
接着,再跑一遍 dijkstra,就可以结束了。
再考虑回一般情况。
我们还是一样,枚举
大家应该都知道,这个取模有两种情况:
因为
于是我们就以
此时,只使用节点
- 对于前缀节点
i+n ,表示的节点是1 \sim i 这个整体。 - 对于后缀节点
i+2n ,表示的节点是i \sim n 这个整体。
模仿特殊情况的连边方式,稍微改一改。
因为对于两个前缀节点
所以,我们可以让
同理,我们也可以让
对于一个节点
至于从整体节点连到单个节点,不管是
正确性:像我们之前说的,对于两个前缀节点
总结(上图,一图胜千言):
这个图只是举个例子,因为淡绿色的边不能污染了其他颜色的边,所以这里只举了部分连边例子。
但是我们发现:边权可能为负!
考虑怎么让边权变为自然数。我们发现,只有当单个节点连向后缀节点的时候,才会产生负数的边。
于是,我们考虑将
于是,我们走一条边
这样,我们的
诶?等等?
这不就完美解决了嘛?
上图(不再展示单个节点连向前缀节点的边):
其中,红色的边强调该边权有更改。
代码(应该有可读性吧):
#include<bits/stdc++.h>
#define x0 x_0
#define x1 x_1
#define y0 y_0
#define y1 y_1
#define yn y_n
#define j0 j_0
#define j1 j_1
#define k0 k_0
#define k1 k_1
#define d0 d_0
#define d1 d_1
#define LL long long
#define LD long double
#define ZPB push_back
#define ZPF push_front
#define US unsigned
using namespace std;
struct node{
int a,b,id;
bool operator < (const node &o)const{return (b!=o.b ? b<o.b : id<o.id);}
};
struct Enode{
int v;
LL w;
bool operator < (const Enode &o)const{return w>o.w;}
};
/*
[1,n]: real_node
[n+1,2n]: qjh_node
[2n+1,3n]: hjh_node
*/
node val[200010];
int n,m,st,ed;
const LL LLmex=1e18;
LL dp[600010];
vector<Enode> G[600010];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>val[i].a,val[i].id=i;
for(int i=1;i<=n;++i) cin>>val[i].b;
sort(val+1,val+n+1);
for(int i=1;i<=n;++i){
if(val[i].id==1) st=i;
if(val[i].id==n) ed=i;
G[n+i].ZPB({i,val[i].b}),
G[(n<<1)+i].ZPB({i,0});
}
for(int i=2;i<=n;++i) G[n+i].ZPB({n+i-1,0});
for(int i=1;i<n;++i) G[(n<<1)+i].ZPB({(n<<1)+i+1,val[i+1].b-val[i].b});
for(int i=1;i<=n;++i){
int L=1,R=n,ans=n+1;
while(L<=R){
int mid=(L+R)>>1;
if(val[i].a+val[mid].b>=m) R=mid-1,ans=mid;
else L=mid+1;
}
if(ans>1) G[i].ZPB({n+ans-1,val[i].a});
if(ans<=n) G[i].ZPB({(n<<1)+ans,val[i].a+val[ans].b-m});
}
priority_queue<Enode> q;
for(int i=1;i<=(n<<1)+n;++i) dp[i]=LLmex;
dp[st]=0,q.push({st,0});
while(q.size()){
Enode dt=q.top();
q.pop();
int x=dt.v;
if(dp[x]<dt.w) continue;
for(int i=0;i<(int)G[x].size();++i){
int u=G[x][i].v;
LL w=G[x][i].w;
if(dp[u]>dp[x]+w) dp[u]=dp[x]+w,q.push({u,dp[u]});
}
}
cout<<dp[ed]<<"\n";
return 0;
}