P3970 [TJOI2014]上升子序列

链接

题意

给你一个大小为n的序列,让你求上升子序列的个数,其中不能算上重复的元素,长度至少为2。

思路

这个跟之前做的那个P2344 [USACO11FEB]Generic Cow Protests G有点类似,这两个题的实质都是一个dp。
回到这个题,假设我们设dp[i]表示以第i个点为止,所求得的方案数,那么如果我们直接暴力,可以在O( N 2 N^2 N2)的时间复杂度内计算得到答案,但是这个题的标签是个数据结构,那么就可以往线段树或者树状数组上面靠(我目前只会这两个)。
题目中说了如果了上升子序列相同,那么只计算一次,意思是会存在重复的元素,那么就想到了离散化,但是我们如果单纯的离散化然后去重,直接计算的话,可能会漏掉一些情况,假如给你一个样例是2 3 5 1 2 4 5,我们去重后会得到2 3 5 1 4,得到的去重后的情况肯定就漏掉了一些情况,应为对于相同的两个数,如果这两个数之间还存在比第一次出现之前的那些数还小的情况,我们会直接忽略掉,显然是不能忽略掉的,那么我们不如单独来维护到位一个为止,所存在的方案,假设这个数已经出现过了,那么更新了答案之后直接将上一次出现的那个位置的方案数减去即可。然后我们就用树状数组添加即可。

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 1e5 + 10;
const int mod = 1e9 + 7;

int n;
int tr[N];
int lans[N], a[N], b[N], vis[N];
map<int, int> cnt;

int lowbit(int x) { return x & -x; }

void add(int x, int k) { for (int i = x; i <= n; i += lowbit(i)) tr[i] = (tr[i] + k) % mod; }

int Sum(int x)
{
    int sum = 0;
    for (int i = x; i; i -= lowbit(i)) sum = (sum + tr[i]) % mod;
    return sum;
}

signed main()
{
    std::ios::sync_with_stdio(false);

    cin >> n;
    for (int i = 1; i <= n; i ++) { cin >> a[i]; b[i] = a[i]; }
    ///离散化去重
    sort(b + 1, b + 1 + n);
    int ans = 0;
    int tot = unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= tot; i ++) cnt[b[i]] = i;

    for (int i = 1; i <= n; i ++)
    {
        int tmp = cnt[a[i]];
        int val = Sum(tmp - 1);

        if (!vis[tmp])
        {
            ans = (ans + val) % mod;
            add(tmp, val + 1);
            lans[tmp] = val;
            vis[tmp] = 1;
            continue;
        }

        ans = (ans + val - lans[tmp]) % mod;
        add(tmp, (val - lans[tmp]) % mod);
        lans[tmp] = val;
    }

    cout << ans << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值