Loading

D - Pyramid 最长上升子数组(连续子序列)

https://atcoder.jp/contests/abc336/tasks/abc336_d

题目大意

给定一个数组 \(a\),求经过以下操作可以得到的最长的金字塔序列。
金字塔序列形如 \(1,2,3,4,5,...,k-1,k,k-1,...5,4,3,2,1\)
可进行的操作为:

  1. 将某数减1
  2. 将开头或末尾的数移除

900分左右的题目。我先口胡一下我想到的做法,首先二分长度 \(k\),然后每次检查的时候开一个线段树,每次这个长度为 \(2\times k-1\) 的区间往后移动的时候其实是可以进行区间加 / 减贡献的,然后每次我检查这个区间有没有小于 \(0\) 的数就可以了,复杂度外面二分加上线段树 \(O(NlogNlogN)\),线段树需要实现区间加和查找第一个小于 \(0\) 的数,并不好写而且很明显这个不是正解。

考虑 \(l_i\) 代表 \(i\) 往左最多能走多少,同理 \(r_i\) 代表 \(i\) 最多能往右走多少,那么答案就是 \(\forall i,\ max(min(l_i,\ r_i))\)。算\(l_i, \ r_i\) 的时候相当于利用了一个 \(dp\) 的思想,\(l_i=min(l_{i-1}+1, \ a_i),\ r_i=min(r_{i+1},\ a_i)\)

感觉做思维题太少一下想不到简单的方法,虽然能乱搞搞过去但是很浪费时间。

import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws Exception {
        int n = nextInt();
        int[] a = new int[n+1];
        for (int i = 1; i <= n; i++) a[i] = nextInt();
        int[] l = new int[n+1], r = new int[n+2];
        for (int i = 1; i <= n; i++) l[i] = Math.min(l[i-1] + 1, a[i]);
        for (int i = n; i >= 1; i--) r[i] = Math.min(r[i+1] + 1, a[i]);
        int ans = 0;
        for (int i = 1; i <= n; i++) ans = Math.max(ans, Math.min(l[i], r[i]));
        cout.println(ans);
        closeAll();
    }
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static int nextInt() throws Exception{ cin.nextToken(); return (int) cin.nval; }
    public static void closeAll() throws Exception { cout.close(); in.close(); out.close(); }
}
posted @ 2024-03-05 22:12  KakaDBL  阅读(26)  评论(0)    收藏  举报