T1 [JZOJ6310] Global warming

题目描述

  给定整数 $n$ 和 $x$,以及一个大小为 $n$ 的序列 $a$。

  你可以选择一个区间 $[l,r]$,然后令 $a[i]+=d \; (l \leq i \leq r)$,其中 $d$ 满足 $|d| \leq x$。

  要求最大化 $a$ 的最长上升子序列的长度,并输出该值。

数据范围

  对于 $5\%$ 的数据点,$n,x \leq 10$

  对于另外 $10\%$ 的数据点,$n,x \leq 50$

  对于另外 $13\%$ 的数据点,$n \leq 1000$

  对于另外 $10\%$ 的数据点,$x=0$

  对于另外 $20\%$ 的数据点,$x \leq 5$,$n \leq 5 \times 10^4$

  对于另外 $17\%$ 的数据点,$x=10^9$

  对于 $100\%$ 的数据点,$n \leq 2 \times 10^5$,$x \leq 10^9$

分析

  $Subtask$ 真是让人头大,玄学挂了两个点结果只得了 $62 \, pts$

  这题有几个很显然的地方

  令区间 $[l,r] \; (1 \leq l \leq r < n)$ 加上 $i \; (0 \leq i \leq x)$ 必不优于区间 $[l,n]$

  令区间 $[l,r] \; (1 < l \leq r \leq n)$ 减去 $i \; (0 \leq i \leq x)$ 必不优于区间 $[1,r]$

  令区间 $[l,n]$ 加上 $i \; (0 \leq i < x)$ 或令区间 $[1,r]$ 减去 $i$ 必不优于加上/减去 $x$

  由于加减实际上是等价的,所以我们假定让区间 $[1,r] \; (1 \leq r < n)$ 减去 $x$

  设 $f_i$ 表示减小的区间为 $[1,i-1]$ 且 $a_i$ 被选中时的最长上升子序列长度

  首先正序做一遍最长上升子序列可以处理出 $f$ 数组,然后倒序做一遍找出最优解

  若当前位置为 $i$,则 $ans_i=f_i \, +$ 以 $i$ 结尾的最长下降子序列长度(倒序)

2019-08-20 纪中NOIP模拟A组
#include <iostream>
#include <cstdio>
#include <cstdlib> 
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 200005

int n, x, pos, ans;
int a[N], q[N], f[N];

int main() {
    scanf("%d%d", &n, &x);
    for (int i = 1; i <= n; i++) scanf("%d", a + i);
    for (int i = 1; i <= n; i++) {
        f[i] = lower_bound(q + 1, q + q[0] + 1, a[i]) - q - 1;
        pos = lower_bound(q + 1, q + q[0] + 1, a[i] - x) - q;
        q[pos] = a[i] - x; q[0] = max(q[0], pos);
    }
    q[0] = 0;
    for (int i = n; i; i--) {
        pos = lower_bound(q + 1, q + q[0] + 1, a[i], greater<int>()) - q;
        q[pos] = a[i]; q[0] = max(q[0], pos);
        ans = max(ans, f[i] + pos);
    }
    printf("%d", ans);
    
    return 0;
}
View Code

相关文章: