AtCoder Beginner Contest 338
D - Island Tour
题目链接
题意:
(小日子的英语真的看不懂,也听不懂QAQ)
有 N N N 座岛和 N N N 座桥,第 i i i 座桥连接第 i i i 个岛和第 i + 1 i+1 i+1 个岛(第 N N N 座桥连接第 1 1 1 个岛和第 N N N 个岛)。一位旅行者想要从岛 X 1 X_1 X1 出发,按顺序游览岛 X 2 , X 3 , . . . , X M X_2,X_3,...,X_M X2,X3,...,XM 。
现在删掉一座桥,问现在游览这些岛需要经过多少座桥?
思路:
假设从岛 l l l 走到岛 r r r,因为从 l l l 走到 r r r 和从 r r r 走到 l l l 经过的桥数是一样,不妨假设 l < r l<r l<r。如果没删桥,那么有两种方式从岛 l l l 走到岛 r r r,即 l → r l\rightarrow r l→r 或 l → 1 → n → r l\rightarrow 1 \rightarrow n \rightarrow r l→1→n→r ,两种方式经过的路线长度分别为 r − l r-l r−l 和 n + l − r n+l-r n+l−r。没删桥的时候肯定两者取其小,但是删掉桥的话,删掉的桥一定在两者其一的路径上,如果在长的路径上就没有影响,但是如果在短的路径上就会导致绕远路。
话句话说,删掉短路径上的桥,会导致吃到一个绕远路的代价,那么我们可以给所有短路径上的桥都记录一下代价是多少,把 M − 1 M-1 M−1 条游览路线都算一遍累加起来,那么删掉一个桥只看代价之和就知道删掉它之后的总的路线长度为多少。最后只需要枚举一下所有桥取最小代价即可。
当然不可能枚举短路径上的所有桥去加代价,太慢了,所以我们使用差分数组。如果原本要走 l → r l\rightarrow r l→r ,那么给路径上的桥加代价 t = n + l − r − ( r − l ) t=n+l-r-(r-l) t=n+l−r−(r−l),那么 c o s t [ s ] + = t , c o s t [ t + 1 ] − = t cost[s]+=t,cost[t+1]-=t cost[s]+=t,cost[t+1]−=t。如果原本要走 l → 1 → n → r l\rightarrow 1 \rightarrow n \rightarrow r l→1→n→r ,那么给路径上的桥加代价 t = r − l − ( n + l − r ) t=r-l-(n+l-r) t=r−l−(n+l−r),那么 1 ∼ l 1\sim l 1∼l 和 r ∼ n r\sim n r∼n 都要加代价,在差分数组上就是 c o s t [ 1 ] + = t , c o s t [ l + 1 ] − = t , c o s t [ r ] + = t , c o s t [ n + 1 ] − = t cost[1]+=t,cost[l+1]-=t,cost[r]+=t,cost[n+1]-=t cost[1]+=t,cost[l+1]−=t,cost[r]+=t,cost[n+1]−=t。
code:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int n,m;
int des[maxn];
ll cst[maxn];//删除i->i+1边的代价
ll ans=1e18,lst=0;
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>des[i];
for(int i=2,l,r,t;i<=m;i++){
l=min(des[i],des[i-1]);
r=max(des[i],des[i-1]);
if(r-l<=n+l-r){//原本走l->r
t=n+l-r-(r-l);
lst+=r-l;
cst[l]+=t;
cst[r]-=t;
}
else {
t=r-l-(n+l-r);
lst+=n+l-r;
cst[r]+=t;
cst[1]+=t;
cst[l]-=t;
}
}
for(int i=1;i<=n;i++){
cst[i]+=cst[i-1];
ans=min(ans,cst[i]);
}
cout<<lst+ans;
return 0;
}