【校内模拟】lis(线段树)(暴力)

讲个笑话,数据结构压轴题96行写完。

由于没有标算,我也不知道出题人的实现写了多长。。。

题解:

按照下发的题解模拟即可

我记得区间的LIS是不能直接做的,如果能的话,这道题就没什么难度了。。。

但是这道题有两个限制:新加入的 h ≤ 10 h\leq 10 h10,删除的数的位置 ≤ 10 \leq 10 10

也就是说,随着树木的增长,比当前加入的 h h h小的最多只有 10 10 10个。

这启示我们使用暴力。

f i f_i fi表示以 i i i开头的LIS长度。

考虑怎么更新,显然需要满足两个条件,在 i i i后面,且比 i i i高。

对于插入,由于高度比当前小的只有不超过 10 10 10个,直接开一个数据结构以位置为下标维护答案,每次暴力将这十个删除,现在能够保证数据结构里面只有高度比当前元素高的,直接按照位置询问即可,再一个个插回去。

对于删除,由于位置在当前这个之前的只有不超过 10 10 10个,直接开一个数据结构以高度为下标维护答案,与上面同理每次暴力将这十个删除再一个个插回去。

发现我们对于数据结构的操作是单点修改,区间询问max,上ZKW线段树。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

struct SGT{
	static cs int N=1<<18|1;
	int a[N],M;
	inline void init(int n){for(M=1;M<=n+1;M<<=1);}
	inline void set(int p,int v){for(a[p+=M]=v,p>>=1;p;p>>=1)a[p]=std::max(a[p<<1],a[p<<1|1]);}
	inline int query(int l,int r){
		int ans=0;
		for(l+=M-1,r+=M+1;l^r^1;l>>=1,r>>=1){
			if(l&1^1)ans=std::max(ans,a[l^1]);
			if(r&1)  ans=std::max(ans,a[r^1]);
		}
		return ans;
	}
}S1,S2;

cs int N=1e5+10;

int n,m,Mx;

struct atom{int p,h;};
struct cmp1{bool operator()(cs atom &a,cs atom &b){return a.p<b.p;}};
struct cmp2{bool operator()(cs atom &a,cs atom &b){return a.h<b.h;}};
std::set<atom,cmp1> sp;
std::set<atom,cmp2> sh;

atom st[15];int tot;
inline void ins(){
	int p=gi(),h=gi()+m+1;
	atom a=(atom){p,h};tot=0;
	auto it=sh.insert(a).first;sp.insert(a);
	while(true){
		st[++tot]=*it;S1.set(it->p,0);
		if(it==sh.begin())break;--it;
	}
	for(int re i=1;i<=tot;++i){
		int val=S1.query(st[i].p,n)+1;
		S1.set(st[i].p,val);
		S2.set(st[i].h,val);
	}
}

inline void del(){
	int x=gi();tot=0;
	auto it=sp.begin();
	while(--x)st[++tot]=*it++;
	S1.set(it->p,0);S2.set(it->h,0);
	sh.erase(*it),sp.erase(it);
	for(int re i=tot;i;--i)S2.set(st[i].h,0);
	for(int re i=tot;i;--i){
		int val=S2.query(st[i].h,Mx)+1;
		S1.set(st[i].p,val);
		S2.set(st[i].h,val);
	}
}

signed main(){
#ifdef zxyoi
	freopen("lis.in","r",stdin);
#endif
	n=gi(),m=gi();S1.init(n),S2.init(Mx=m+20);
	while(m--){
		switch(gi()){
			case 1:ins();break;
			case 2:del();break;
		}
		cout<<S1.a[1]<<"\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值