CF559E Gerald and Path 思考--zhengjun

做了半天,然后打开题解发现里面全是 O(n3)/O(n2) 的。

然后我的原来 O(n5) 的前缀 max 优化成 O(n4) 的就非常🤡。

为了区分 [l,r] 中的 l 和第 i 个线段的长度 li,令 bi 表示第 i 个线段的长度。

O(n4) 做法

首先发现最后的答案是若干线段长度的和。

而且每个线段所覆盖的端点是连续的。

所以先按照端点坐标排序,然后考虑分两步 dp:

  1. 计算 fl,r,i 表示 [l,r] 内拼成左端点为 i 的线段右端点的最大值为多少。

  2. 计算 gi,j 表示 [1,i] 最后一个线段的右端点在 j 的最大总长度。

dp1

只需要考虑 i=akbk,k[l,r]i=al,然后枚举分成两段能否合并即可。

此时确定左端点的区间,每个线段肯定是选择 [ai,ai+bi] 更优。

使用前缀和优化做到 O(n4)

dp2

没啥好讲的,暴力转移即可,分段 dp 一个模样。

O(n3)

发现根本不用区间 dp。

直接从小到大考虑,设 fi,j 表示前 i 个的最右边为 j 的最大长度。

然后考虑转移:

  • i 无效:fi1,jfi,j

    这里无效的意思是可以剔除他使得答案不变。

  • i 向右:fi1,j+wfi,max{j,Ri}
  • i 向左:fi1,j+wfi,max{j,ai}

这样写发现会有问题,原因在于这种转移:

     i         k
------ <--------
------     -------->
           j
  • 转移:fi,j+wfk,max{ak,Rj},LkRj,i<j<k

O(n2)

O(n3) 的加上个前缀和优化即可。

情况 1

     i         k
------ <--------
           -------->
           j

k 处找到最大的 Rj 满足 Lk<aj<ak<Rj,查询右端点 <Lk 的最大值即可。

使用前缀 max 优化。

情况 2

               k
       <--------
---------  -------->
        i  j

j 处找到向后第一个 LkikO(n2) 预处理后可以直接查询。

然后直接转移到 fk,Rj 即可。

代码

O(n4)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e2+10,M=N*3,INF=1e9;
int n,m,c[M],a[N],L[N],R[N];
struct seg{
	int x,y;
}t[N];
int f[N][N][M],g[N][M],sf[N][N][M];
void chkmax(int &x,int y){
	if(x<y)x=y;
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>t[i].x>>t[i].y;
	for(int i=1;i<=n;i++)c[++m]=t[i].x,c[++m]=t[i].x-t[i].y,c[++m]=t[i].x+t[i].y;
	sort(t+1,t+1+n,[](seg x,seg y){return x.x<y.x;});
	sort(c+1,c+1+m),m=unique(c+1,c+1+m)-c-1;
	auto trs=[&](int &x){
		x=lower_bound(c+1,c+1+m,x)-c;
	};
	for(int i=1;i<=n;i++)trs(L[i]=t[i].x-t[i].y),trs(R[i]=t[i].x+t[i].y),trs(a[i]=t[i].x);
	memset(f,-0x3f,sizeof f),memset(sf,-0x3f,sizeof sf);
	for(int i=1;i<=n;i++){
		f[i][i][a[i]]=R[i],f[i][i][L[i]]=a[i];
		for(int j=1;j<=m;j++)chkmax(sf[i][i][j]=f[i][i][j],sf[i][i][j-1]);
	}
	for(int l=n;l>=1;l--){
		for(int r=l+1;r<=n;r++){
			for(int i=l;i<=r;i++){
				if(L[i]>a[l])continue;
				int mx=a[i];
				for(int j=l;j<=r;j++){
					if(mx<a[j])break;
					if(j^i)chkmax(mx,R[j]);
					if(j>=i){
						if(j<r)chkmax(f[l][r][L[i]],sf[j+1][r][mx]);
						else chkmax(f[l][r][L[i]],mx);
//						for(int k=1;k<=mx;k++){
//							chkmax(f[l][r][L[i]],f[j+1][r][k]);
//						}
					}
				}
			}
			int mx=a[l];
			for(int i=l;i<=r;i++){
				if(mx<a[i])break;
				chkmax(mx,R[i]);
				if(i<r)chkmax(f[l][r][a[l]],sf[i+1][r][mx]);
				else chkmax(f[l][r][a[l]],mx);
//				for(int k=1;k<=mx;k++){
//					chkmax(f[l][r][a[l]],f[i+1][r][k]);
//				}
			}
			for(int i=1;i<=m;i++)chkmax(sf[l][r][i]=f[l][r][i],sf[l][r][i-1]);
		}
	}
	memset(g,-0x3f,sizeof g);
	g[0][0]=0;
	for(int l=1;l<=n;l++){
		for(int r=l;r<=n;r++){
			for(int i=0;i<=m;i++){
				for(int j=i+1;j<=m;j++){
					if(f[l][r][j]<0)continue;
					chkmax(g[r][f[l][r][j]],g[l-1][i]+c[f[l][r][j]]-c[j]);
				}
			}
		}
	}
	int ans=-INF;
	for(int i=1;i<=m;i++)chkmax(ans,g[n][i]);
	cout<<ans;
	return 0;
}

O(n4)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e2+10,M=N*3,INF=1e9;
int n,m,c[M],a[N],L[N],R[N];
struct seg{
	int x,y;
}t[N];
int f[N][M];
void chkmax(int &x,int y){
	if(x<y)x=y;
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>t[i].x>>t[i].y;
	for(int i=1;i<=n;i++)c[++m]=t[i].x,c[++m]=t[i].x-t[i].y,c[++m]=t[i].x+t[i].y;
	sort(t+1,t+1+n,[](seg x,seg y){return x.x<y.x;});
	sort(c+1,c+1+m),m=unique(c+1,c+1+m)-c-1;
	auto trs=[&](int &x){
		x=lower_bound(c+1,c+1+m,x)-c;
	};
	for(int i=1;i<=n;i++)trs(L[i]=t[i].x-t[i].y),trs(R[i]=t[i].x+t[i].y),trs(a[i]=t[i].x);
	memset(f,-0x3f,sizeof f);
	f[0][0]=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<=m;j++){
			if(f[i][j]<0)continue;
			chkmax(f[i+1][j],f[i][j]);
			int mx=j;
			for(int k=i+1;k<=n;k++){
				int x=max(R[k],j);
				chkmax(f[k][x],f[i][j]+c[x]-c[max(j,a[k])]);
				x=max(a[k],j);
				chkmax(f[k][x],f[i][j]+c[x]-c[max(j,L[k])]);
				if(L[k]<=mx){
					x=max(mx,a[k]);
					chkmax(f[k][x],f[i][j]+c[x]-c[max(j,L[k])]);
				}
				chkmax(mx,R[k]);
			}
		}
	}
	int ans=-INF;
	for(int i=0;i<=m;i++)ans=max(ans,f[n][i]);
	cout<<ans;
	return 0;
}

O(n2)

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e2+10,M=N*3,INF=1e9;
int n,m,c[M],a[N],L[N],R[N];
struct seg{
	int x,y;
}t[N];
int f[N][M],g[M];
int nex[N][M];
void chkmax(int &x,int y){
	if(x<y)x=y;
}
int main(){
	freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>t[i].x>>t[i].y;
	for(int i=1;i<=n;i++)c[++m]=t[i].x,c[++m]=t[i].x-t[i].y,c[++m]=t[i].x+t[i].y;
	sort(t+1,t+1+n,[](seg x,seg y){return x.x<y.x;});
	sort(c+1,c+1+m),m=unique(c+1,c+1+m)-c-1;
	auto trs=[&](int &x){
		x=lower_bound(c+1,c+1+m,x)-c;
	};
	for(int i=1;i<=n;i++)trs(L[i]=t[i].x-t[i].y),trs(R[i]=t[i].x+t[i].y),trs(a[i]=t[i].x);
	memset(nex,-1,sizeof nex);
	for(int i=n;i>=1;i--){
		for(int j=1;j<L[i];j++)nex[i][j]=nex[i+1][j];
		for(int j=L[i];j<=m;j++)nex[i][j]=i;
	}
	memset(f,-0x3f,sizeof f),memset(g,-0x3f,sizeof g);
	f[0][0]=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=m;j++){
			chkmax(f[i][j],f[i-1][j]);
			int x=max(R[i],j);
			chkmax(f[i][x],f[i-1][j]+c[x]-c[max(j,a[i])]);
			x=max(a[i],j);
			chkmax(f[i][x],f[i-1][j]+c[x]-c[max(j,L[i])]);
			if(a[i]>j){
				int k=nex[i+1][j];
				if(~k&&a[k]<R[i])chkmax(f[k][R[i]],f[i-1][j]+c[R[i]]-c[j]);
			}
		}
		int mx=0;
		for(int j=i-1;j>=1&&a[j]>=L[i];j--)chkmax(mx,R[j]);
		if(mx>a[i])chkmax(f[i][mx],g[L[i]-1]+c[mx]-c[L[i]]);
		for(int j=0;j<=m;j++)chkmax(g[j],f[i][j]);
		for(int j=1;j<=m;j++)chkmax(g[j],g[j-1]);
	}
	int ans=-INF;
	for(int i=0;i<=m;i++)ans=max(ans,f[n][i]);
	cout<<ans;
	return 0;
}
posted @   A_zjzj  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示