P2075 区间 LIS 题解

前置题目:回转寿司

题目大意

给定一个序列,求区间LIS。

解题思路

确定LIS的求法

LIS 有两种常见的求法,一种是直接DP求,另一种是近乎贪心的策略,具体的,就是每次遇到一个数 \(x\),考虑将其加入LIS中,有两种处理:1.找到序列里第一个比他大的数,替换到。2.如果无法找到比他大的数,加到LIS末尾。两种做法都是 \(O(n \log n)\) 的。虽然这个过程的序列并非真实的LIS序列,但是可以证明,这个序列的长度必然是最长的LIS的长度。不妨把这个序列记为 \(S\)

显然DP无法解决,因此考虑使用贪心的做法。

计算答案

考虑改变一下贪心的做法,尝试维护 \(S\) 那么当遇到数字 \(x\) 时,肯定插入。具体的,若有比 \(x\) 大的数,那么就去掉其中最小的比 \(x\) 大的数字,否则直接插入。

因此,定义 \(S_{l,r}\) 表示区间 \([l,r]\) 内的 \(S\) 。考虑对 \(r\) 进行扫描线。不难发现,相邻的两个 \(l\)\(S_{l,r}\) 最多只会相差 \(1\) 。这里的 1 指的既是元素,也是大小。证明显然。

加速计算

再思考一下,显然对于相同的 \(r\)\(x\) 必定出现在一段连续的区间 \(S_{l,r}\) 中。因此设 \(a_x\) 表示最大的 \(l\) ,满足数字 \(x\) 出现在 \(S_{l,r}\) 中。那么答案就是 \(a_x \ge l\) 的个数。

考虑让 \(r\) 变为 \(r+1\),令 \(v = p_r\) ,那么 \(a_v = r\) ,对于 \(a_{v+1}\),如果其出现过,那么肯定被 \(v\) 给换出去。然后对于 \(a_{v+2}\),其能存活,当且仅当 \(a_{v+1}\) 存在于 \(S\) 中,否则其就要被换出去了。

推广一下,就变成了:令 \(x = 0\),从 \(i = v+1\) 开始,依次遍历 \(a_i\) ,如果 \(a_i > x\) ,那么就交换 \(a_i,x\)

感性理解一下,就是 \(v\) 的加入就是在 gank 比它大的数字。从 \(v+1\) 开始,依次消灭。但是对于每一个数字,比它小的数字便会成为它的“名刀”,可以代替他抗下这次消灭。因此,只要“名刀”覆盖了其的存在区间,那么就不会被换出,否则必然被换出,而他也将成为后面的人的“名刀”。实在难以理解的话,手完一下,就能理解了。

然后问题变成了 P14400(题解先鸽着)。做完了。

Code

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 1e5 + 10,B = 317;
priority_queue< int > qmax[B];
priority_queue< int , vector<int> , greater<int> > tag[B];
priority_queue< int , vector<int> , greater<int> > gummu;
int n,Q;
int p[N];
struct Ques{
	int l,r;
	int id;
}ques[N];
int ans[N];
bool cmp(Ques x,Ques y){
	if(x.r == y.r) return x.l < y.l;
	return x.r < y.r;
}
int a[N];
struct BIT{
	int tre[N];
	BIT(){memset(tre,0,sizeof tre);return ;}
	int lowbit(int x){return x & (-x);}
	void update(int x,int k){
		while(x){
			tre[x] += k;
			x -= lowbit(x);
		}
		return ;
	}
	int query(int x){
		int sum = 0;
		while(x <= n){
			sum += tre[x];
			x += lowbit(x);
		}
		return sum;
	}
}tre;
int bl[N],L[B],R[B];
int cnt;
priority_queue< int > get(int l,int r){
	priority_queue< int > re;
	for(int i=l;i<=r;i++)
		re.push(a[i]);
	return re;
}
void rebuild(int x){
	if(tag[x].empty()) return;
	for(int i=L[x];i<=R[x];i++){
		if(a[i] > tag[x].top()){
			int t = tag[x].top();
			tag[x].pop();
			tag[x].push(a[i]);
			a[i] = t;
		}
	}
	tag[x] = gummu;
	return ;
}
int query(int l,int r,int x){
	int lx = bl[l],rx = bl[r];
	if(lx == rx){
		rebuild(lx);
		for(int i=l;i<=r;i++)
			if(a[i] > x) swap(a[i],x);
		qmax[lx] = get(L[lx],R[lx]);
		return x;
	}
	rebuild(lx);
	rebuild(rx);
	for(int i=l;i<=R[lx];i++)
		if(a[i] > x) swap(a[i],x);
	qmax[lx] = get(L[lx],R[lx]);
	for(int i=lx+1;i<=rx-1;i++)
		if(qmax[i].top() > x){
			int t = qmax[i].top();
			qmax[i].pop();
			tag[i].push(x);
			qmax[i].push(x);
			x = t;
		}
	for(int i=L[rx];i<=r;i++)
		if(a[i] > x) swap(a[i],x);
	qmax[rx] = get(L[rx],R[rx]);
	return x;
}
int main()
{
	IOS;
	cin >> n >> Q;
	cnt = (n + B - 1) / B;
	for(int i=1;i<=n;i++){
		cin >> p[i];
		bl[i] = (i + B - 1) / B;
		qmax[bl[i]].push(0);
	}
	for(int i=1;i<=cnt;i++){
		L[i] = R[i-1] + 1;
		R[i] = B * i;
	}
	R[cnt] = n;
	for(int i=1;i<=Q;i++){
		cin >> ques[i].l >> ques[i].r;
		ques[i].id = i;
	}
	sort(ques+1,ques+1+Q,cmp);
	int nw = 1;
	for(int r=1;r<=n;r++){
		int v = p[r];
		if(v < n){
			int x = query(v+1,n,0);
			tre.update(x,-1);
		}
		rebuild(bl[v]);
		a[v] = r;
		tre.update(a[v],1);
		qmax[bl[v]] = get(L[bl[v]],R[bl[v]]);
		while(nw <= Q && ques[nw].r == r){
			ans[ques[nw].id] = tre.query(ques[nw].l);
			nw ++ ;
		}
	}
	for(int i=1;i<=Q;i++)
		cout << ans[i] << "\n";
	return 0;
}
posted @ 2026-01-26 16:17  WinterXorSnow  阅读(5)  评论(0)    收藏  举报