P3970 [TJOI2014]上升子序列

· · 题解

关于此题的一个新的方法:

此题其实不需要其他题解的 last 数组来记录什么的,整体代码长度也比较短。

关键部分的代码:

  for (int i = 1; i <= n; i++) {
    int val = ask(num[i]) - ask(num[i] - 1);
    add(num[i], ask(num[i] - 1));
    if (val) add(num[i], -val + 1);
    else add(num[i], 1);
  }

解释如下:

$add()$,$ask()$ 对应的是树状数组的基操, 对于如何建立树状数组,我们可以这样来考虑: 建立数组 $c[i]$ 保存**当前以 $i$ 结尾的上升子序列的集合的元素个数**。 ------------ 如何维护 $c[i]$?: 1.遍历 $num[]$。 2.如果当前的数对应的集合为空,则要将 $ c[i] $ 加一,让它变成 $ \{i\}$ 只有一个 $i$ 元素,方便后续转移。 3.遍历到 $i$ 时,我们将它加上 $c[1]$ 到 $c[num[i]-1]$ 的和,这应该是比较好理解的, 但是若 $c[i]$ 本身有值,我们会发现出现了重复,此时把 $c[i]$ 对应集合的元素全重新计算了一遍,所以要**减去 $c[i]$ 原本的值**。 4.注意 $\{i\}$ 没有被重复计算,所以还要 $+1$。 ------------ 所以答案就是: ` cout << ask(m) - m; ` 因为 $\{i\}$ 不算做上升序列。 CODE: ```cpp #include <bits/stdc++。h> #define int long long #define mod 1000000000 + 7 #define N 100007 using namespace std; int n, m; int num[N], c[N], ls[N]; void add(int x, int v) { for (; x <= m; x += x & -x) { c[x] += v; c[x] %= mod; } } int ask(int x) { int ret = 0; for (; x; x -= x & -x) { ret += c[x]; ret %= mod; } return ret; } signed main() { ios::sync_with_stdio(false); cin >> n; for (int i = 1; i <= n; i++) { cin >> num[i]; ls[i] = num[i]; } sort(ls + 1, ls + n + 1); m = unique(ls + 1, ls + n + 1) - (ls + 1); for (int i = 1; i <= n; i++) num[i] = lower_bound(ls + 1, ls + 1 + m, num[i]) - ls; for (int i = 1; i <= n; i++) { int val = ask(num[i]) - ask(num[i] - 1); add(num[i], ask(num[i] - 1)); if (val) add(num[i], -val + 1); else add(num[i], 1); } cout << ask(m) - m; return 0; } ```