F - Prefix LIS Query

题目链接:https://atcoder.jp/contests/abc393/tasks/abc393_f

题意:

给定一个序列,q个询问
每个循环给定一个R和X
请找出在[1,R]序列前缀范围的且最大值不超过X的LIS

思路:

对于所有询问考虑离线查询
规定f[i]为长度为i的LIS的最小结尾值
(对于以下的最优LIS实际上就是以i作结尾的线性dp)
从头开始维护f数组
需要用到nlogn的LIS做法
(即使用辅助数组b,来记录当前状态下的最优最长上升子序列)
对于遍历到的a[j],如果大于最优LIS的最大值,则pushback,并且表示当前最大长度的LIS的最小结尾值为a[j]
反之,如果小于最优LIS的最小值,那么二分更新序列第一个大于等于它的值,并且更新对应长度的最小结尾值
由于f数组记录最小结尾值,且易知f数组是单调递增的。所以可以二分查找f值第一个小于等于x的下标,便是该次查询的答案

int f[maxn];
void solve(){
	int n,len=1;
	int a[maxn];
	vector<int>b;
 	int q;
	cin>>n>>q;
	vector<pair<pii,int>>s;
	rep(i,1,n) {
		cin>>a[i];
	}
	
	rep(i,1,q){
		int r,x;cin>>r>>x;
		s.pb({{r,x},i});
	}
	
	sort(s.begin(),s.end());
	b.pb(a[1]);
	int st=2;
	f[1]=a[1];
	vector<int>ans(q+1);
	int c=1;
	for(int i=0;i<q;i++){
		int r=s[i].fi.fi,x=s[i].fi.se,index=s[i].se;
		for(int j=st;j<=r;j++){
			if(a[j]>b.back()){
				b.pb(a[j]);f[++c]=a[j];
			}else{
				int pos=lower_bound(b.begin(),b.end(),a[j])-b.begin();
				b[pos]=a[j];
				f[pos+1]=a[j];
			}
		}
		
		int lt=0,rt=b.size(),res=0;
		while(lt<=rt){
			int mid=lt+rt>>1;
			if(f[mid]>x){
				rt=mid-1;
			}else{
				res=mid;
				lt=mid+1;
			}
		}
		
		st=r+1;
		ans[index]=res;
	}
	
	rep(i,1,q){
		cout<<ans[i]<<endl;
	}
}
posted @ 2025-04-02 22:00  Marinaco  阅读(23)  评论(0)    收藏  举报
//雪花飘落效果