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;
}

浙公网安备 33010602011771号