現在看這題居然直接秒了。。。去年看的時候還以為神題。。
設以第i項為結尾的lis前綴為f[i],以第j項為結尾的lis后綴為g[i],如果求出f[i]和g[j],然后枚舉i,快速找到最大的滿足a[j]>a[i]的g[j]就可以了。注意到如果將f[i]從后往前枚舉,那么只要添加g[j]而不用刪除操作了,因此枚舉f[i],在線段樹中找(a[i]+1,Xn]中g的最大值就可以了,ans=f[i]+max(g[j]) (a[j]>a[i]且j>i+L),然后順勢把g[j]插入線段樹。
求f[i]也是dp+線段樹優化,f[i]=max(f[j])+1 (a[j]<a[i])。


#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define key_val ch[ch[rt[i]][1]][0] #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1using namespace std;typedef long long ll; const int maxn=1000100; const int INF=1e9+10;int n,L; int a[maxn],X[maxn],Xn; int f[maxn],g[maxn]; int Max[maxn<<2];void push_up(int rt) {Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); }void build(int l,int r,int rt) {if(l==r){Max[rt]=0;return;}int m=(l+r)>>1;build(lson);build(rson);push_up(rt); }void update(int p,int c,int l,int r,int rt) {if(l==r){Max[rt]=max(Max[rt],c);return;}int m=(l+r)>>1;if(p<=m) update(p,c,lson);else update(p,c,rson);push_up(rt); }int query(int L,int R,int l,int r,int rt) {if(L>R) return 0;if(L<=l&&r<=R) return Max[rt];int m=(l+r)>>1;int res=0;if(L<=m) res=max(res,query(L,R,lson));if(R>m) res=max(res,query(L,R,rson));return res; }int main() {#ifndef ONLINE_JUDGEfreopen("in.txt","r",stdin);#endifint T;cin>>T;REP(casen,1,T){scanf("%d%d",&n,&L);REP(i,1,n) scanf("%d",&a[i]),X[i]=a[i];sort(X+1,X+n+1);Xn=unique(X+1,X+n+1)-(X+1);REP(i,1,n) a[i]=lower_bound(X+1,X+Xn+1,a[i])-X;build(1,Xn,1);f[0]=0;REP(i,1,n) f[i]=query(1,a[i]-1,1,Xn,1)+1,update(a[i],f[i],1,Xn,1);build(1,Xn,1);int ans=0,tmp=0;for(int i=n;i>=1;i--){int j=i-L;if(j>=0){tmp=f[j]+query(a[j]+1,Xn,1,Xn,1);ans=max(ans,tmp);}g[i]=query(a[i]+1,Xn,1,Xn,1)+1;update(a[i],g[i],1,Xn,1);}printf("Case #%d: %d\n",casen,ans);}return 0; }
?