[Tjoi2016&Heoi2016] 序列 CDQ分治
题意:
佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值可能会变化,但同一个时刻最多只有一个值发生变化。
现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可。
注意:每种变化最多只有一个值发生变化。
在样例输入1中,所有的变化是
1 2 3
2 2 3
1 3 3
1 1 3
1 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:
3 3 3
3 2 3
选择子序列为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求。
输入:
输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。
接下来一行有n个数,表示这个数列原始的状态。
接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。
1 ≤ x ≤ n
输出:
输出一个整数,表示对应的答案
思路:
看到最长不下降子序列首先想的是用树状数组优化\(dp\),但这道题相较于正常的\(LIS\)多了一个改变,实际上也就是多加了几个限制。
对于第\(i\)位和第\(j\)位如果选择\(j\)接在\(i\)后面,原来只用考虑\(i<j\)以及\(a_i<a_j\),这题要满足变化其中一个值后仍成立,所以需要考虑的有:
1、\(i<j\)
2、\(a_i<=Mi[j]\)
3、\(Mx[i]<=a_j\)
此时题目就变成了一个三维偏序问题,可以考虑用CDQ求解了。
即在外面维护条件1,CDQ中根据条件2排序,用树状数组直接维护条件3
但由于等号两边比较的东西不同,所以相较以前的写法,这里对于\([l,mid]\)和\([mid+1,r]\)的排序方式是不一样的。也就是对于左边区间直接用值进行升序排列,右区间根据Mi升序排列。
而树状数组中每次在Mx的位置上进行更新,查询时则根据val
应该没什么细节,只要不字母打错之类的
#include<bits/stdc++.h>
#define M 100005
#define lowbit(x) (x&-x)
using namespace std;
int n,m,ans[M];
struct node {
int id,x,mx,mi;//最大可以变成的 和最小可以变成的值
void add(int y) {
mx=max(mx,y),mi=min(mi,y);
}
} a[M],p[M];
struct Tree {
int cnt[M];
void add(int x,int y) {
while(x<M)cnt[x]=max(cnt[x],y),x+=lowbit(x);
}
int Query(int x) {
int res=0;
while(x)res=max(res,cnt[x]),x-=lowbit(x);
return res;
}
void del(int x) {
while(x<M)cnt[x]=0,x+=lowbit(x);
}
} T;
bool cmp_x(node A,node B) {
if(A.x!=B.x)return A.x<B.x;
return A.mx<B.mx;
}
bool cmp_mi(node A,node B) {
if(A.mi!=B.mi)return A.mi<B.mi;
return A.x<B.x;
}
bool cmp_id(node A,node B){
return A.id<B.id;
}
void CDQ(int l,int r) {
while(l>=r)return;
int mid=(l+r)>>1;
CDQ(l,mid);
sort(a+l,a+mid+1,cmp_x);
sort(a+mid+1,a+r+1,cmp_mi);
int j=l;
for(int i=mid+1; i<=r; i++) {
while(j<=mid&&a[j].x<=a[i].mi) {
T.add(a[j].mx,ans[a[j].id]);
j++;
}
int t=T.Query(a[i].x);
ans[a[i].id]=max(ans[a[i].id],t+1);
}
for(int i=l; i<j; i++)T.del(a[i].mx);//撤销
// sort(a+mid+1,a+r+1,cmp_id);
for(int i=mid+1; i<=r; i++)a[i]=p[i];//还原成id升序排列
CDQ(mid+1,r);
}
int main() {
// freopen("data.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)scanf("%d",&a[i].x),a[i].mi=a[i].mx=a[i].x,a[i].id=i,ans[i]=1;//因为不管怎么样序列最短都有1
for(int i=1; i<=m; i++) {
int x,y;
scanf("%d%d",&x,&y);
a[x].add(y);
}
for(int i=1; i<=n; i++)p[i]=a[i];
int Ans=0;
CDQ(1,n);
for(int i=1; i<=n; i++)Ans=max(Ans,ans[i]);
printf("%d\n",Ans);
return 0;
}