CF559E Gerald and Path 思考--zhengjun

发布时间 2023-08-07 18:29:18作者: A_zjzj

做了半天,然后打开题解发现里面全是 \(O(n^3)/O(n^2)\) 的。

然后我的原来 \(O(n^5)\) 的前缀 \(\max\) 优化成 \(O(n^4)\) 的就非常?。

为了区分 \([l,r]\) 中的 \(l\) 和第 \(i\) 个线段的长度 \(l_i\),令 \(b_i\) 表示第 \(i\) 个线段的长度。

\(O(n^4)\) 做法

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

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

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

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

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

dp1

只需要考虑 \(i=a_k-b_k,k\in[l,r] \or i=a_l\),然后枚举分成两段能否合并即可。

此时确定左端点的区间,每个线段肯定是选择 \([a_i,a_i+b_i]\) 更优。

使用前缀和优化做到 \(O(n^4)\)

dp2

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

\(O(n^3)\)

发现根本不用区间 dp。

直接从小到大考虑,设 \(f_{i,j}\) 表示前 \(i\) 个的最右边为 \(j\) 的最大长度。

然后考虑转移:

  • \(i\) 无效:\(f_{i-1,j}\to f_{i,j}\)

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

  • \(i\) 向右:\(f_{i-1,j}+w\to f_{i,\max\{j,R_i\}}\)
  • \(i\) 向左:\(f_{i-1,j}+w'\to f_{i,\max\{j,a_i\}}\)

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

     i         k
------ <--------
------     -------->
           j
  • 转移:\(f_{i,j}+w''\to f_{k,\max\{a_k,R_j\}},L_k\le R_j,i<j<k\)

\(O(n^2)\)

\(O(n^3)\) 的加上个前缀和优化即可。

情况 1

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

\(k\) 处找到最大的 \(R_j\) 满足 \(L_k<a_j<a_k<R_j\),查询右端点 \(<L_k\) 的最大值即可。

使用前缀 \(\max\) 优化。

情况 2

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

\(j\) 处找到向后第一个 \(L_k\le i\)\(k\)\(O(n^2)\) 预处理后可以直接查询。

然后直接转移到 \(f_{k,R_j}\) 即可。

代码

\(O(n^4)\)

#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(n^4)\)

#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(n^2)\)

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