题目链接
题目解法
考虑暴力 d i j dij dij 时间会爆炸,可以考虑优化边数,使其变成 O ( n ) O(n) O(n) 级别
有一个重要的
t
r
i
c
k
trick
trick 是把
(
a
u
+
b
v
)
m
o
d
m
(a_u+b_v)\;mod\;m
(au+bv)modm 变为
(
−
(
−
a
u
)
+
b
v
)
m
o
d
m
(-(-a_u)+b_v)\;mod\;m
(−(−au)+bv)modm,这样的好处是把
u
,
v
u,v
u,v 之间的边权变成了在
0
−
m
0-m
0−m 组成的环上的距离,仔细考虑一下会发现这是对的
如果令
0
0
0 到
m
−
1
m-1
m−1 为环,
m
m
m 到
m
+
n
−
1
m+n-1
m+n−1 为原始点的话,所以可以考虑建如下的边:
- 对于 i ∈ [ 0 , m ) , i\in[0,m), i∈[0,m), i i i 到 i + 1 i+1 i+1建一条权值为 1 的边
- 对于 i ∈ [ m , m + n ) i\in[m,m+n) i∈[m,m+n), i i i 到 − a i -a_i −ai 建一条权值为 0 的边
- 对于 i ∈ [ m , m + n ) i\in[m,m+n) i∈[m,m+n), b i b_i bi 到 i i i 建一条权值为 0 的边
考虑这样的图上 m m m 到 m + n − 1 m+n-1 m+n−1 的最短路即为答案
考虑有极大多数点是无用的,考虑只建 − a i , b i -a_i,b_i −ai,bi 和原始点这 3 n 3n 3n 个点,然后类似连边跑 d i j dij dij 即可,可以发现,这样的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn) 的
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int> pii;
const int N(600100);
int n,m,cnt,a[N],b[N],disc[N],dis[N];
bool vis[N];
int e[N<<1],h[N],ne[N<<1],w[N<<1],idx;
priority_queue<pii,vector<pii>,greater<pii> > que;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void dij(){
memset(dis,0x3f,sizeof(dis));
que.push(make_pair(0,cnt+1));
dis[cnt+1]=0;
while(!que.empty()){
int u=que.top().second;que.pop();
if(vis[u]) continue;
// cout<<u<<' '<<dis[u]<<'\n';
vis[u]=1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(dis[u]+w[i]<dis[v]){
dis[v]=dis[u]+w[i];
que.push(make_pair(dis[v],v));
}
}
}
}
void add(int x,int y,int z){ e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;}
signed main(){
memset(h,-1,sizeof(h));
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read(),a[i]=(m-a[i])%m;
for(int i=1;i<=n;i++) b[i]=read();
for(int i=1;i<=n;i++) disc[++cnt]=a[i],disc[++cnt]=b[i];
sort(disc+1,disc+cnt+1);
cnt=unique(disc+1,disc+cnt+1)-disc-1;
// for(int i=1;i<=cnt;i++) cout<<disc[i]<<' ';cout<<'\n';
for(int i=1;i<cnt;i++) add(i,i+1,disc[i+1]-disc[i]);
add(cnt,1,m-disc[cnt]+disc[1]);
for(int i=1;i<=n;i++){
a[i]=lower_bound(disc+1,disc+cnt+1,a[i])-disc;
b[i]=lower_bound(disc+1,disc+cnt+1,b[i])-disc;
add(i+cnt,a[i],0),add(b[i],i+cnt,0);
}
dij();
printf("%lld",dis[cnt+n]);
return 0;
}