BZOJ 3173 最长上升子序列(树状数组+二分+线段树)
给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
由于序列是顺序插入的,所以当前插入的数字对之前的数字形成的最长上升子序列没有任何影响,所以只需要计算出当前的这个数字结尾的上升子序列长度。
由于$dp[i]=max(dp[j])+1(j<i)$,所以可以用线段树维护。
这样就需要预先计算出来这个序列的最后的状态,考虑从n到1倒着算,二分这个数字出现的位置。
因此总时间复杂度为$O(nlogn)$.
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <bitset> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-8 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FDR(i,a,n) for(int i=a; i>=n; --i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; namespace IO{ char buf[1<<18], *fs, *ft; inline char readc(){ return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++; } inline int Scan(){ char c; int r; while(c = readc()){if(isdigit(c)){r = c^0x30;break;}} while(isdigit(c = readc()))r = (r<<3)+(r<<1)+(c^0x30); return r; } inline int Scan_s(char *str){ int len = 1;char c; while(!isalpha(c = readc()));str[0] = c; while(isalpha(c = readc()))str[len++] = c; str[len] = 0; return len; } };using IO::Scan_s; using IO::Scan; const int N=100005; //Code begin... int a[N], b[N], tree[N], seg[N<<3], ans[N]; void add(int x){while (x<N) ++tree[x], x+=lowbit(x);} int sum(int x){ int res=0; while (x) res+=tree[x], x-=lowbit(x); return res; } void push_up(int p){seg[p]=max(seg[p<<1],seg[p<<1|1]);} void update(int p, int l, int r, int L, int val){ if (L>r||L<l) return ; if (L==l&&L==r) seg[p]=val; else { int mid=(l+r)>>1; update(lch,L,val); update(rch,L,val); push_up(p); } } int query(int p, int l, int r, int L, int R){ if (L>r||R<l) return 0; if (L<=l&&R>=r) return seg[p]; int mid=(l+r)>>1; return max(query(lch,L,R),query(rch,L,R)); } int main () { int n; scanf("%d",&n); FOR(i,1,n) scanf("%d",a+i), ++a[i]; FDR(i,n,1) { int l=a[i], r=n+1, mid; while (l<r) { mid=(l+r)>>1; if (sum(mid)<=mid-a[i]) r=mid; else l=mid+1; } a[i]=r; add(a[i]); } FOR(i,1,n) ans[i]=query(1,1,n,1,a[i])+1, update(1,1,n,a[i],ans[i]); FOR(i,1,n) ans[i]=max(ans[i],ans[i-1]); FOR(i,1,n) printf("%d\n",ans[i]); return 0; }