树状数组+dp
因为今天复习离散化于是手贱加了个离散化
题目大意
意思是给你一段序列,求里面的最长不下降子序列的个数。
dp思想
这道题的dp方程非常的好推,看完题目的第一眼就已经推出了方程
设dp[i]表示以当前点为终点的序列方案。所以方程是
[dp[i] += (i>j&&a[i]ge a[j])? dp[j]:0\
ans=sum_{i=1}^{n}(dp[i]+1)
]
但是一看这种方法就是(n^2)的复杂度,明显过不了,那怎么办呢?
优化
我们知道,我们最后求得一定是一个前缀和的形式,所以这个树状数组可以做的很好。
我们先将原数组从小到大排个序,然后寻找它的下标。
为什么从小到大呢?因为从小到大的话我们求得一定是不下降子序列,那么决定答案的就是原位置的下标。
我们可以二分查找它的下标,那么在他下标之前的前缀和一定就是总的方案数。
然后在查询的前缀合里加1,就是答案。
不理解?举个例子。
排序前: 1 2 5 4 3
排序后: 1 2 3 4 5
数组下标: 1 2 5 4 3
首先第一个值是1,它得下标是1,前缀是0,所以方案数是1
第二个值2,下标二,前缀和1,方案数2
下个值3,下标是3,位置是5,前缀和是3,方案数4
下个值4,下标是4,位置是4,前缀和是4,方案数是5
下个值5,下标是5,位置是3,前缀和是3,方案数是4
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int mod = 1000000007;
int c[100001];
int n;
int b[100001],cnt;
LL tree[100001];
int a[100001];
bool cmp(int x,int y) {
return x<y;
}
int lowbit(int k) {
return k&(-k);
}
void add(int k,int val) {
while(k<=n) {
tree[k]+=val;
k+=lowbit(k);
}
}
int ask(int k) {
int ans=0;
while(k!=0) {
ans = (ans+tree[k])%mod ;
k-=lowbit(k);
}
return ans%mod;
}
int main() {
while(scanf("%d",&n)!=EOF) {
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(tree,0,sizeof(tree));
cnt=0;
for(int i=1; i<=n; i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n,cmp);
for(int i=1; i<=n; i++) if(b[i]!=b[cnt])b[++cnt]=b[i];
for(int i=1; i<=n; i++)a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
for(int i=1; i<=n; i++)c[i]=a[i];
sort(a+1,a+1+n,cmp);
for(int i=1; i<=n; i++) {
int id=lower_bound(a+1,a+1+n,c[i])-a;
add(id,ask(id)+1);
}
printf("%d
",ask(n));
}
}