题目:
求一个数列的最长上升子序列和。 (1≤n≤105,ai是32位整数) ( 1 ≤ n ≤ 10 5 , a i 是 32 位 整 数 )
分析:
其实这个题的数据没有那么大,n^2的算法也可以过,在此提供一种O(nlogn)的做法。
转移方程是dp[i] = max(dp[j]) + a[i], (1<= j < i, a[j] < a[i])
用线段树可以将找最大 dp[j] 的过程从O(n)变为O(nlogn)
对数据离散化,然后每个数据都不会超过n的大小。对每一个i,可以用线段树查找 1 到 i-1 的最大值,然后用这个值加上 a[i] 即可。
更新的时候把dp[i]这个值更新进离散化后 a[i] 代表的点即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
#define lson rt*2,l,(l+r)/2
#define rson rt*2+1,(l+r)/2+1,r
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 1e5+5;
const double EPS=1e-8;
const int INF=0x3f3f3f3f;
const int MOD = 1e9+7;
int n, tree[MAXN << 2], a[MAXN];
void pushup(int rt){
tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void update(int a,int b,int rt,int l,int r){
if(l==r){
tree[rt]=max(tree[rt],b);
return;
}
if(a<=(l+r)/2) update(a,b,lson);
else update(a,b,rson);
pushup(rt);
}
int query(int L,int R,int rt,int l,int r){
if(L<=l && R>=r){
return tree[rt];
}
if(R<=(l+r)/2) return query(L,R,lson);
else if(L>(l+r)/2) return query(L,R,rson);
else return max(query(L,R,lson),query(L,R,rson));
}
int main(){
ios::sync_with_stdio(false);
// freopen("1.txt","r",stdin);
int c[MAXN];
while(cin >> n && n){
for(int i=1;i<=n;i++){
cin >> a[i];
c[i] = a[i];
}
ms(tree,0);
sort(c+1,c+1+n);
int m = unique(c+1,c+n+1)-(c+1);
int ans = -INF, Max = -INF;
for(int i=1;i<=n;i++){
int p = lower_bound(c+1,c+1+m,a[i]) - c;
if(p == 1) {
Max = 0;
update(1,a[i],1,1,m);
}
else{
Max = query(1,p-1,1,1,m);
update(p,Max+a[i],1,1,m);
}
ans = max(ans,Max+a[i]);
}
cout << ans << endl;
}
return 0;
}