F - Removed Interval(最长上升子序列)

题意:删去连续的长度为L的序列,求最长上升子序列。严格上升。
dp[i][0]代表选择i点在[1,i]中找最长上升子序列。
dp[i][1]代表删去了长度为L的连续序列之后选择i点的在[1,i]中的最长上升子序列。
离散化加线段树维护求区间最大值。
坑点:忘了我最后找的状态是选择了第n个点的dp[n][1],可是还有可能是因为dp[n+1][1],即最后删除n-L+1到n这一段连续序列之后的最长上升子序列。真坑,wc。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAX_N=101000;
struct node{
 	int l,r,max0,max1,max2;
}a[MAX_N*4];
void update(int k){
 	a[k].max0=max(a[k<<1].max0,a[k<<1|1].max0);
 	a[k].max1=max(a[k<<1].max1,a[k<<1|1].max1);
 	a[k].max2=max(a[k<<1].max2,a[k<<1|1].max2);
}
void build(int k,int l,int r){
 	a[k].l=l;a[k].r=r;
 	if(l==r){
  		a[k].max0=0;
  		a[k].max1=0;
  		a[k].max2=0;
  		return;
 	}
 	int mid=(l+r)>>1;
 	build(k<<1,l,mid);
 	build(k<<1|1,mid+1,r);
 	update(k);
}
void change0(int k,int x,int y){
 	if(a[k].l==a[k].r){
  		a[k].max0=max(y,a[k].max0);
  		return;
 	}
 	int mid=(a[k].l+a[k].r)/2;
 	if(x<=mid)
 	change0(k<<1,x,y);
 	else
 	change0(k<<1|1,x,y);
 	update(k);
}
void change1(int k,int x,int y){
 	if(a[k].l==a[k].r){
  		a[k].max1=max(y,a[k].max1);
  		return;
 	}
 	int mid=(a[k].l+a[k].r)/2;
 	if(x<=mid)
 	change1(k<<1,x,y);
 	else
 	change1(k<<1|1,x,y);
 	update(k);
}
void change2(int k,int x,int y){
 	if(a[k].l==a[k].r){
  		a[k].max2=max(y,a[k].max2);
  		return;
 	}
 	int mid=(a[k].l+a[k].r)/2;
 	if(x<=mid)
 	change1(k<<1,x,y);
 	else
 	change1(k<<1|1,x,y);
 	update(k);
}
int query0(int k,int l,int r){
 	if(a[k].l>=l&&a[k].r<=r)
 	return a[k].max0;
 	int mid=(a[k].l+a[k].r)>>1;
 	int x=0;
 	if(r>mid)
 	x=max(x,query0(k<<1|1,l,r));
 	if(l<=mid)
 	x=max(x,query0(k<<1,l,r));
 	return x;
}
int query1(int k,int l,int r){
 	if(a[k].l>=l&&a[k].r<=r)
 	return a[k].max1;
 	int mid=(a[k].l+a[k].r)>>1;
 	int x=0;
 	if(r>mid)
 	x=max(x,query1(k<<1|1,l,r));
 	if(l<=mid)
 	x=max(x,query1(k<<1,l,r));
 	return x;
}
int query2(int k,int l,int r){
 	if(a[k].l>=l&&a[k].r<=r)
 	return a[k].max2;
 	int mid=(a[k].l+a[k].r)>>1;
 	int x=0;
 	if(r>mid)
 	x=max(x,query2(k<<1|1,l,r));
 	if(l<=mid)
 	x=max(x,query2(k<<1,l,r));
 	return x;
}
int dp[101000][2];
int aa[101000],sub_a[101000],b[101000];
int main(void){
	int T,n,L,i;
	cin>>T;
	int t=T;
	while(T--){
		scanf("%d%d",&n,&L);
		//dp[0][0]=0;dp[0][1]=0;
		for(i=1;i<=n;i++){
			dp[i][0]=0;
			dp[i][1]=0;
			scanf("%d",&aa[i]);
			sub_a[i-1]=aa[i];
		}
		sort(sub_a,sub_a+n);
		int nn=unique(sub_a,sub_a+n)-sub_a;
		for(i=1;i<=n;i++){
			b[i]=lower_bound(sub_a,sub_a+nn,aa[i])-sub_a+1;
			//cout<<b[i]<<"\n";
		}
		b[0]=0;
		build(1,0,n);
		int ans=0;
		for(i=1;i<=n;i++){
			dp[i][0]=query0(1,0,b[i]-1)+1;
			//ans=max(ans,dp[i][0]);
			change0(1,b[i],dp[i][0]);
			if(i>L){
                change1(1,b[i-L-1],dp[i-L-1][0]);
				//dp[i][1]=dp[i-L-1][0]+1;
				dp[i][1]=query1(1,0,b[i]-1)+1;
				dp[i][1]=max(dp[i][1],query2(1,0,b[i]-1)+1);
				change2(1,b[i],dp[i][1]);
				ans=max(ans,dp[i][1]);
			}
		}
		ans=max(ans,dp[n-L][0]);
		printf("Case #%d: %d\n",t-T,ans);
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值