「CTSC2017」最长上升子序列 做题记录

tag:杨表

Dilworth定理:最小链覆盖等于最长反链。
一个杨表的性质:将比较方式取反,所得的杨表跟原表的转置 形状 相同。

根据杨表相关知识,排列的最长 \(k-LIS\) 子串长度为其前 \(k\) 列的高度和。
这道题里不是排列,但是这个结论依然成立,半标准杨表在一些性质上其实跟标准杨表没有什么区别?
但是实际上不能完全理解这样建出来究竟是啥,因为在要求上升并且数集有重时,可能出现 行满足上升,列满足不下降 的情况,跟半标准杨表的定义还是有出入的。

根据 Dilworth 定理,序列的最长上升子序列长度 等于 将序列划分成若干个不上升子序列,数量的最小值。
那么,要求的实际上是 取不超过 \(k\) 个不上升子序列,能取的最大元素个数。
这个可以用杨表求,即杨表的前 \(k\) 行长度和,转置一下,也就相当于上面的结论。

离线询问,那我们要做的是维护杨表,支持查询前 \(k\) 列长度和。
\(O(n^2)\) 的暴力复杂度很难接受,注意到可能很长的只有前 \(\sqrt n\) 行和前 \(\sqrt n\) 列,并且同一个数不可能同时不在这两部分之内。
那么只用维护这两个就可以了。
\(\sqrt n\) 列不好维护,利用上面的性质,维护其转置的形态即可。


#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
namespace io{
	#define gc (fp==fq&&(fq=(fp=fr)+fread(fr,1,1<<20,stdin))==fp?EOF:*fp++)
	char fr[1<<21],*fp=fr,*fq=fr,ch;
	int read()
	{
		while (!isdigit(ch=gc));int x=ch^48;
		while (isdigit(ch=gc))x=x*10+(ch^48);
		return x;
	}
}
using io::read;
using namespace std;
const int N=5e4+10, M=300, Q=2e5+10;
int n,T,len[M],b[M][N],tr[N],a[N],ans[Q],c[M][N],h[M];
struct que{int t,k,id;}q[Q];
void add(int x){
	while(x<n){
		++tr[x];
		x+=(x&(-x));
	}
}
int query(int x){
	int ret=0;
	while(x){
		ret+=tr[x];
		x-=(x&(-x));
	}
	return ret;
}
bool cmp(que a,que b){return a.t<b.t || (a.t==b.t && a.id<b.id);}
void insert1(int x){
	int it=len[1],l,r,i=1;
	for(;i<=230;++i){
		if(!len[i] || x>b[i][len[i]]){
			b[i][++len[i]]=x;
			if(len[i]>230)add(len[i]);
			return;
		}
		int mid;l=0;r=min(it,len[i]);
		while(l<r-1){
			mid=l+r>>1;
			if(b[i][mid]>=x)r=mid;
			else l=mid;
		}
		it=r;
		swap(b[i][it],x);
	}
}
void insert2(int x){
	int it=h[1],l,r,i=1;
	for(;i<=230;++i){
		if(!h[i] || x>=c[i][h[i]]){
			c[i][++h[i]]=x;
			add(i);
			return;
		}
		int mid;l=0,r=min(it,h[i]);
		while(l<r-1){
			mid=l+r>>1;
			if(c[i][mid]>x)r=mid;
			else l=mid;
		}
		it=r;
		swap(c[i][it],x);
	}
}
int main(){
	freopen("data.in","r",stdin);
	freopen("data.out","w",stdout);
	n=read();T=read();
	fo(i,1,n)a[i]=read();
	fo(i,1,T)q[i].t=read(),q[i].k=read(),q[i].id=i;
	sort(q+1,q+T+1,cmp);
	int it=1;
	fo(i,1,n){
		insert1(a[i]);insert2(-a[i]);
		for(;it<=T && q[it].t==i;++it)ans[q[it].id]=query(q[it].k);
	}
	fo(i,1,T)printf("%d\n",ans[i]);

	return 0;
}
posted @ 2022-04-06 20:18  Kelvin2005  阅读(64)  评论(0)    收藏  举报