CF650D - Zip-line——离线、BIT

D - Zip-line

题意翻译

给定一个长度为 n n n 的数组 a a a m m m 次询问,每次询问如果把某位置的值换成另一个给定值,此数组的最长上升子序列(LIS)长度为多少。注意到询问间是独立的,不会真的进行修改。

题解

此题没有强制在线,所以我们不妨把询问离线下来。

离线下来后,我们就可以用我们熟知的树状数组(BIT)维护最大值的方法,先正着求一遍每个点以前的最长上升子序列,再倒着求一遍下降的子序列,中途就可以顺便在值域树状数组上处理包含询问位置的最长上升子序列。

然而仅仅时这样的话,还过不了样例一。我们发现上面的过程只算了包含询问节点的LIS,没算不包含询问节点的LIS。

但是这并不是什么大问题,我们可以分类讨论然后解决。容易发现不包含节点 i i i 的LIS只有3种情况:

  • 前面第一个点比 a i a_i ai 小,后面第一个点比 a i a_i ai 大。这种情况很明显就是包含点 i i i 的LIS长度-1;
    在这里插入图片描述
  • 前面第一个点 ≥ a i ≥a_i ai。此时我们设前面第一个点为 j j j,显然包含点 j j j 的LIS必然不会包含点 i i i。所以只需要再次用权值BIT对每个点 i i i 求出 max ⁡ j < i , a j ≥ a i LIS j \max_{j<i,a_j≥a_i}\text{LIS}_j maxj<i,ajaiLISj
    在这里插入图片描述
  • 后面第一个点 ≤ a i \le a_i ai。设后面第一个点为 j j j,同样地,包含 j j j 的LIS必然不包含点 i i i,所以倒着用权值BIT求出 max ⁡ j > i , a j ≤ a i LIS j \max_{j>i,a_j\le a_i}\text{LIS}_j maxj>i,ajaiLISj
    在这里插入图片描述

最后还需要统计一下答案。复杂度 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn)

代码

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#define ll long long
#define uns unsigned
#define MAXN 400005
#define INF 1e18
#define MOD 9901ll
#define lowbit(x) ((x)&(-(x)))
#define IF it->first
#define IS it->second
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,m,k,h[MAXN],f[MAXN],ans[MAXN];
int pr[MAXN],sf[MAXN],mx[MAXN];
map<int,int>mp; //笔者习惯于用map离散化,虽然慢,但是方便又灵活
map<int,int>::iterator it;
inline void add(int x,int d){
	for(;x<=k;x+=lowbit(x))f[x]=max(f[x],d);
}
inline int sch(int x){
	int res=0;
	for(;x>0;x-=lowbit(x))res=max(res,f[x]);
	return res;
}
struct itn{
	int h,i;itn(){}
	itn(int H,int I){h=H,i=I;}
};
vector<itn>as[MAXN];
signed main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)h[i]=read(),mp[h[i]]=1;
	for(it=mp.begin();it!=mp.end();it++)IS=++k;
	for(int i=1;i<=n;i++)h[i]=mp[h[i]];
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		as[x].push_back(itn(y,i));
	}
	for(int i=1;i<=k;i++)f[i]=0;
	for(int i=1;i<=n;i++){
		for(uns j=0;j<as[i].size();j++){
			int a=as[i][j].h;
			it=mp.lower_bound(a);
			if(it==mp.begin())continue;
			it--,a=IS;
			ans[as[i][j].i]+=sch(a);
		}
		pr[i]=sch(h[i]-1);
		add(h[i],pr[i]+1);
	}
	for(int i=1;i<=k;i++)f[i]=0;
	for(int i=n;i>0;i--){
		for(uns j=0;j<as[i].size();j++){
			int a=as[i][j].h;
			it=mp.lower_bound(a+1);
			if(it==mp.end())continue;
			a=IS;
			ans[as[i][j].i]+=sch(k-a+1);
		}
		sf[i]=sch(k-h[i]);
		add(k-h[i]+1,sf[i]+1);
	}
	for(int i=1;i<=n;i++)mx[i]=pr[i]+sf[i];
	for(int i=1;i<=k;i++)f[i]=0;
	for(int i=1;i<=n;i++){
		mx[i]=max(mx[i],sch(k-h[i]+1));
		add(k-h[i]+1,pr[i]+sf[i]+1);
	}
	for(int i=1;i<=k;i++)f[i]=0;
	for(int i=n;i>0;i--){
		mx[i]=max(mx[i],sch(h[i]));
		add(h[i],pr[i]+sf[i]+1);
	}
	for(int i=1;i<=n;i++)
		for(uns j=0;j<as[i].size();j++){
			int d=as[i][j].i;
			ans[d]=max(ans[d],mx[i]-1);
		}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]+1);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值