HDU 2227 Find the nondecreasing subsequences

树状数组+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));
	}
}
原文地址:https://www.cnblogs.com/ifmyt/p/9713705.html