STO CXR ORZ
首先套路地把所有关键点离散化,并且把所有线段按照端点从小到大排序
我们发现我们可以把一条线段([l,r])拆成([l,l],[l,l+1],cdots,[l,r]),此时我们就可以强制每条线段不相交了
设(f_{i,j})表示前(i)条线段,上一条的右端点最远在(j)的最大覆盖,考虑每条线段的情况无非是两种选择:
- 向右,这种情况非常简单,直接大力转移即可
- 向左,主要要考虑在之前的点中存在一条线段的右端点超过当前点。我们可以枚举一个分界点,强制左边的不会超过这个分界点,并且右边的强制向右求出最远的位置
考虑这样做的正确性,假设存在反例的话就是存在一个点在分界点左侧选择向右,一个点在分界点右侧选择向左
但是画一画我们发现这样一定不如改成在分界点左侧选择向左,一在分界点右侧选择向右更优,于是正确性得证
总复杂度(O(nlog n))
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
struct element
{
int p,l;
friend inline bool operator < (const element& A,const element& B)
{
return A.p<B.p;
}
}a[N]; int n,rst[3*N],cnt,f[N][3*N],mr[N][N];
inline int find(CI x)
{
return lower_bound(rst+1,rst+cnt+1,x)-rst;
}
int main()
{
RI i,j,k; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&a[i].p,&a[i].l),
rst[++cnt]=a[i].p,rst[++cnt]=a[i].p-a[i].l,rst[++cnt]=a[i].p+a[i].l;
sort(rst+1,rst+cnt+1); cnt=unique(rst+1,rst+cnt+1)-rst-1;
for (sort(a+1,a+n+1),i=1;i<=n;++i)
for (mr[i][i]=find(a[i].p+a[i].l),j=i+1;j<=n;++j)
mr[i][j]=max(mr[i][j-1],find(a[j].p+a[j].l)); for (i=1;i<=n;++i)
{
int pos=find(a[i].p),l=find(a[i].p-a[i].l),r=find(a[i].p+a[i].l);
for (j=r;j>=pos;--j) f[i][j]=max(f[i][j],f[i-1][pos]+rst[j]-rst[pos]);
for (j=1;j<=i;++j) for (k=max(pos,mr[j][i-1]);k>=l;--k)
f[i][k]=max(f[i][k],f[j-1][l]+rst[k]-rst[l]);
for (j=1;j<=cnt;++j) f[i][j]=max(f[i][j],f[i-1][j]),f[i][j]=max(f[i][j],f[i][j-1]);
}
return printf("%d",f[n][cnt]),0;
}