ABC338D 题解

· · 题解

题意

# 思路 暴力求解复杂度必炸, 考虑优化。 显然可以把**计算每条路径**转化成**记录每条路径的贡献**。 ------------ ## 1.路径计算 若求两点 $l,r$ 间距离,可以先令 $r \gt l$,方便处理。 我们发现在环上从 $r$ 走到 $l$ 只有两种路径, 且断掉一条边以后为一条链,即只有唯一的路径, 那么我们就可以先求出两种路径分别的长度: - 其中直接从 $1$ - $n$ 内部走(即 $l,l+1 \cdots r-1,r$)的路程即 $(r-l)

通过分析可知两种的路径长度和为环的总长度 n

则第二种的路径长为 (n-(r-l))

2.记录求解

因为最终要求的是 (m-1) 段路程的和最小,

考虑把每一段路程分别计算,最后再一起求和。

a_i 表示断掉连接 ii+1 之间这条边后的总路程,

注意到 $l$ 和 $r$ 将 $a$ 数组分成三部分, 第一部分是 $1$ 至 $l-1$ ,贡献为第一种; 第二部分是 $l$ 至 $r-1$ ,贡献为第二种; 第三部分是 $r$ 至 $n$ ,贡献仍为第一种。 特别地,当 $l=1$ 时,第一部分不存在,需要特殊处理。 因此这三部分的区间加操作可以使用差分数组 $s$ 记录。 最后用前缀和恢复 $a$ 数组,输出最小值即可。 具体看代码实现吧。 # 代码 [我的赛时代码](https://atcoder.jp/contests/abc338/submissions/49723789)~~(写得有点啰嗦)~~ 以下是我重写的~~对新手友好的~~代码。 ```cpp #include<iostream> using namespace std; const int N=2e5+10; int n,m,x[N]; long long ans=1e18,s[N];//初始化,记得开longlong int main() { cin>>n>>m; for(int i=1;i<=m;i++) cin>>x[i]; for(int i=1;i<m;i++)//处理(m-1)段距离的贡献 { int l=x[i]; int r=x[i+1]; if(l>r) swap(l,r);//使r>l int sn=r-l;//第一种的距离 int sw=n-sn;//第二种的距离 if(l==1) s[l]+=sw;//s[l]即s[1],断掉1和2之间的边时只能先到n,即第二种 else { s[1]+=sn; s[l]+=(sw-sn); } s[r]+=(sn-sw); } for(int i=1;i<=n;i++) { s[i]+=s[i-1]; ans=min(ans,s[i]); }//求前缀和及最小值答案 cout<<ans; return 0; } ```