CF559E Gerald and Path 思考--zhengjun
做了半天,然后打开题解发现里面全是 \(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:
-
计算 \(f_{l,r,i}\) 表示 \([l,r]\) 内拼成左端点为 \(i\) 的线段右端点的最大值为多少。
-
计算 \(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;
}