Tree Burning(前后缀和)
题目链接:https://atcoder.jp/contests/agc030/tasks/agc030_b
题意:
在一个有 L 个单位长度的圆上有 n 个点, 小明位于 0 位置的人要按照步骤把 n 个点走完。
圆的单位下标为[0, L-1],每个点有一个位置x[i] 。
步骤:
1、如果所有的点都被走完,游戏结束。
2、小明选择顺时针或者逆时针方向行走。
3、小明按照步骤2 选定的方向行走,直到碰见第一个未走过的点, 继续执行步骤1 。
输出:
小明行走的最远距离。
思路:
已知小明若要行走的距离最远, 必然要左右横跳。
对于到达结束过程的点 i 之前, 小明会在其左边经历 i - 1 个点, 在其右边经历 n - i 个点, 记二者较小值为 num, 则 num 为反复横跳数。
若要行走距离尽可能大, 则要走到尽可能远的地方开始跳。 因此,我们只需要枚举从哪个点开始左右跳,并以此计算左右跳过程中行走的距离,以及最后一步是从左还是从右到达即可。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e5+10; 5 int L, n; 6 ll pre[maxn], a[maxn], suf[maxn]; 7 ll solve() { 8 ll res=a[n]; 9 for (int i=1; i<=n; ++i) pre[i] = pre[i-1]+a[i]; 10 for (int i=n; i>=1; --i) suf[i] = suf[i+1]+L-a[i]; 11 for (int i=1; i<=n; ++i) { 12 int det = n-i, p = i+(det>>1); 13 if(det&1) 14 res = max(res, (suf[p+2]+pre[p]-pre[i-1])*2+L-a[p+1]); 15 else 16 res = max(res, (suf[p+1]+pre[p-1]-pre[i-1])*2+a[p]); 17 } 18 return res; 19 } 20 21 int main() { 22 scanf("%d%d", &L,&n); 23 for (int i=1; i<=n; ++i) scanf("%lld", a+i); 24 ll ans = solve(); 25 reverse(a+1, a+1+n); 26 for (int i=1; i<=n; ++i) a[i] = L-a[i]; 27 printf("%lld\n", max(ans, solve())); 28 return 0; 29 }