[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;
}
posted @ 2019-06-06 13:38  季芊月  阅读(195)  评论(0编辑  收藏  举报