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 }
View Code

 

posted @ 2019-03-10 19:31  Acerkoo  阅读(140)  评论(0编辑  收藏  举报