链接
题意给你一个大小为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#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 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; }


![P3970 [TJOI2014]上升子序列 P3970 [TJOI2014]上升子序列](http://www.mshxw.com/aiimages/31/1011830.png)
