cdq分治 提高篇

优化动态规划

序列

首先要会最长上升子序列的转移,这里就不说了。

我们 \(i\) 位置的初始值为 \(a_i\),可能变成的最大值为 \(mx_i\),可能变成的最小值为 \(mn_i\)

然后如果 \(j\) 要转移到 \(i\),则需要满足:\(j<i,mx_j\le a_i,a_j\le mn_i\)。然后考虑把 \([l,mid]\) 按照 \(mx\) 排序,使得如果出现 \(j\) 满足 \(mx_j>a_i\) 那么 \([l,j-1]\) 是一个极大的区间满足这个区间的数全部满足前两个要求(因为 \(mx\) 单调不降)。还有就是,因为 \([mid+1,r]\) 按照 \(a\) 排序,所以可以保证如果对于区间 \([l,j]\)\(mx\) 值全部不大于 \(i\)\(a\) 值,那么对于 \(k>i\) 也不会出现 \(mx\) 值更大的情况,所以转移是对的。

然后就是怎么转移,大概就是把 \(j\)\(f\) 值扔进树状数组,到转移的时候查一下 \(a\) 值不大于当前点 \(mn\) 值的点的最大 \(f\) 值进行转移(\(f\) 是动态规划转移数组)。

最后看一下代码:

#include<bits/stdc++.h>
#define int long long
#define N 100005
#define lim 100000
using namespace std;
int n,m,a[N],mx[N],mn[N],f[N],p[N],c[N];
bool cmp1(int x,int y){
	return mx[x]<mx[y];
}
bool cmp2(int x,int y){
	return a[x]<a[y];
}
int lowbit(int x){
	return x&-x;
}
void modify(int x,int v){
	while(x<=lim){
		c[x]=max(c[x],v);
		x+=lowbit(x);
	}
}
int qry(int x){
	int res=0;
	while(x){
		res=max(res,c[x]);
		x-=lowbit(x);
	}
	return res;
}
void clear(int x){
	while(x<=n){
		c[x]=0;
		x+=lowbit(x);
	}
}
void cdq(int l,int r){
	if(l==r){
		f[l]=max(f[l],1ll);
		return;
	}
	int mid=l+r>>1;
	cdq(l,mid);
	for(int i=l;i<=r;i++){
		p[i]=i;
	}
	sort(p+l,p+mid+1,cmp1);
	sort(p+mid+1,p+r+1,cmp2);
	int j=l;
	for(int i=mid+1;i<=r;i++){
		while(j<=mid&&mx[p[j]]<=a[p[i]]){
			modify(a[p[j]],f[p[j]]);
			j++;
		}
		f[p[i]]=max(f[p[i]],qry(mn[p[i]])+1);
	}
	for(int i=l;i<=mid;i++){
		clear(a[i]);
	}
	cdq(mid+1,r);
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		mx[i]=mn[i]=a[i];
	}
	while(m--){
		int x,y;
		cin>>x>>y;
		mx[x]=max(mx[x],y);
		mn[x]=min(mn[x],y);
	}
	cdq(1,n);
	int res=0;
	for(int i=1;i<=n;i++){
		res=max(res,f[i]);
	}
	cout<<res;
	return 0;
}

Cool loves touli

这个题和上面的一样非常套路。就是考虑离散化之后,对两边采取对应的关键字进行排序,然后就是按照最长上升子序列的方式进行转移,最后还原就做完了。

这里笔者有个经典错误,把在树状数组用到的后两维离散化了,没有注意到第二维比了大小,然后就一直 \(0\) 分。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 300005
#define lim 300000
using namespace std;
int n,m,b[N],c[N],res;
struct node{
	int l,s,w,a;
	int f;
}a[N];
bool cmpl(node a,node b){
	return a.l<b.l;
}
bool cmps(node a,node b){
	return a.s<b.s;
}
bool cmpa(node a,node b){
	return a.a<b.a;
}
int lowbit(int x){
	return x&-x;
}
void modify(int x,int v){
	while(x<=lim){
		c[x]=max(c[x],v);
		x+=lowbit(x);
	}
}
int qry(int x){
	int res=0;
	while(x){
		res=max(res,c[x]);
		x-=lowbit(x);
	}
	return res;
}
void clear(int x){
	while(x<=lim){
		c[x]=0;
		x+=lowbit(x);
	}
}
void cdq(int l,int r){
	if(l==r){
		a[l].f=max(a[l].f,1ll);
		return;
	}
	int mid=l+r>>1;
	cdq(l,mid);
	sort(a+l,a+mid+1,cmpa);
	sort(a+mid+1,a+r+1,cmps);//事实上这里的关键字取决于while里比大小的两个变量 
	int i=l,j=mid+1;
	while(j<=r){
		while(i<=mid&&a[i].a<=a[j].s){//这里比了大小,所以后三维都要离散化 
			modify(a[i].w,a[i].f);
			i++;
		}
		a[j].f=max(a[j].f,qry(a[j].a)+1);//正常转移
		res=max(res,a[j].f);
		j++;
	}
	for(j=l;j<i;j++)clear(a[j].w);
	sort(a+mid+1,a+r+1,cmpl);//还原 
	cdq(mid+1,r);
}
signed main(){
	cin>>n;
	int cnt=0;
	for(int i=1;i<=n;i++){
		cin>>a[i].l>>a[i].s>>a[i].w>>a[i].a;
		b[++cnt]=a[i].s;
		b[++cnt]=a[i].w;
		b[++cnt]=a[i].a;
	}
	sort(b+1,b+cnt+1);
	m=unique(b+1,b+cnt+1)-b-1;
	for(int i=1;i<=n;i++){
		a[i].s=lower_bound(b+1,b+m+1,a[i].s)-b;
		a[i].w=lower_bound(b+1,b+m+1,a[i].w)-b;
		a[i].a=lower_bound(b+1,b+m+1,a[i].a)-b;
	}
	sort(a+1,a+n+1,cmpl);
	cdq(1,n);
	cout<<res;
	return 0;
}
posted @ 2024-07-26 19:56  zxh923  阅读(5)  评论(0编辑  收藏  举报