「青训营 X 码上掘金」主题四 攒青豆

103 阅读2分钟

当青训营遇上码上掘金

题目

   小白要去青训营中进行为期 x 天的训练,青训营使用特制日历,该日历共有 n 月,每一月的包含的天数不一定相同。小白在每一月的第 i 天可以获得 i青豆。小白在青训营连续呆 x最多可以获得多少青豆

Tips:从最后一个月可以呆到第一个月

输入

  1. 第一行输入 n : 表示该日历共有 n月。 x : 表示小白在青训营训练的天数
  2. 第二行输入 n 个数分别表示每个月的天数

提示

  1. n ≤ 2e5
  2. 1 ≤ month[i] ≤ 1e6
  3. 1 ≤ x ≤ sum(month)

示例

输入

3 2
1 3 1

输出

5

解释

month :{1 3 1} 展开为每一天的收益 => {1 1 2 3 1},连续两天最多可以得到 5 个青豆 

解题思路(贪心、前缀和)

  1. 将month的每一天拉平展开为一个数组,那么该题就可以转换为一个滑窗问题,但是由于x可能很大,所以需要进一步考虑优化
  2. 要让青豆数量最多,那么只需要窗口末尾在月末就行,可以用反证法证明: 假设窗口末尾不在月末是最优的,设窗口末尾在第k 天是最优的,那么右边一定是第k+1天如果窗口向右滑,由于我们假设不在月末更优,因此窗口的元素和减少,所以从左边出去的元素必然大于k+1,出去的元素的左边那个元素必然大于k,那么把窗口改为向左滑,窗口左边进来一个大于k 的数,右边出去一个等于k的数,窗口元素和变大,矛盾,因此窗口末尾一定在月末是最优的。
  3. 双指针模拟窗口滑动

代码

    import java.io.*;
    import java.util.*;

    public class Main {
        static int M = 1000005;
        static long[] sum = new long[M];
        public static void main(String[] args) throws IOException {
            // 收益的前缀和
            for(int i = 1; i < M; i++)
                sum[i] = sum[i - 1] + i;
            Kattio io = new Kattio();
            int n = io.nextInt();
            long x = io.nextLong();
            int[] month = new int[n];
            for(int i = 0 ; i < n; i++)
                month[i] = io.nextInt();
            long ans = x;
            long summ = 0;
            // i当前窗口尾, p当前窗口头
            for(int i = n - 1, p = n - 1; i >= 0; i--){
                while(x > 0){
                    x -= month[p];
                    summ += sum[month[p]];
                    if(p == 0)
                        p = n - 1;
                    else
                        p--;
                }
                // 去掉大于x天的收益
                ans = Math.max(ans, summ - (1-x)*(-x) / 2);
                x += month[i];
                summ -= sum[month[i]];
            }
            io.println(ans);
            io.close();
        }


        public static class Kattio extends PrintWriter {
            private BufferedReader r;
            private StringTokenizer st;

            // 标准 IO
            public Kattio() {
                this(System.in, System.out);
            }

            public Kattio(InputStream i, OutputStream o) {
                super(o);
                r = new BufferedReader(new InputStreamReader(i));
            }

            // 文件 IO
            public Kattio(String intput, String output) throws IOException {
                super(output);
                r = new BufferedReader(new FileReader(intput));
            }

            // 在没有其他输入时返回 null
            public String next() {
                try {
                    while (st == null || !st.hasMoreTokens())
                        st = new StringTokenizer(r.readLine());
                    return st.nextToken();
                } catch (Exception e) {
                }
                return null;
            }

            public int nextInt() {
                return Integer.parseInt(next());
            }

            public double nextDouble() {
                return Double.parseDouble(next());
            }

            public long nextLong() {
                return Long.parseLong(next());
            }
        }
    }