[cf559E]Gerald and Path
将所有线段的端点(即$a_{i}$和$a_{i}\pm l_{i}$)离散,并按照$a_{i}$从小到大排序
定义$f_{i,,j}$表示前$i$条线段在位置$j$之前最多能覆盖的长度(默认覆盖到$j$,允许覆盖到$j$之后,但该部分不计入覆盖的长度),转移对第$i$条线段的方向分类讨论:
(关于"默认覆盖到$j$",完整的描述即默认$[a_{i},j]$已经被覆盖,即之后覆盖不计算贡献)
1.若第$i$条线段向右覆盖,即有
$$
f_{i,j}=\begin{cases}f_{i-1,j}&(j\le a_{i}或a_{i}+l_{i}<j)\\f_{i-1,a_{i}}+(j-a_{i})&(a_{i}<j\le a_{i}+l_{i})\end{cases}
$$
2.若第$i$条线段向左覆盖,即有
$$
f_{i,j}=\begin{cases}f_{i-1,j}&(j\le a_{i}-l_{i})\\f_{i-1,a_{i}-l_{i}}+(j-(a_{i}-l_{i}))&(a_{i}-l_{i}<j\le a_{i})\\\max_{1\le k\le i}\left(f_{k-1,a_{i}-l_{i}}+\min(a_{k}+l_{k},j)-(a_{i}-l_{i})\right) &(a_{i}<j)\end{cases}
$$
这里解释一下第3种情况,考虑枚举最终右端点的位置(如果选$k=i$即不存在,注意此时中间应为$a_{k}$),那么注意到在其之后的直线如果向前覆盖到$a_{i}-l_{i}$之前,即已将整个$[a_{i}-l_{i},a_{i}]$全部覆盖,那么显然一定不如向右覆盖,因此即只需要考虑其之前的线段即可
综上,时间复杂度为$o(n^{3})$,可以通过
事实上,还可以进行优化,复杂度的瓶颈在于向左覆盖时$a_{i}<j$的部分,显然该部分可以用线段树优化(只需要对$a_{k}+l_{k}$和$j$的大小关系分类讨论即可),时间复杂度即降为$o(n^{2}\log n)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 105 4 map<int,int>mat; 5 map<int,int>::iterator it; 6 int n,m,a[N],l[N],pos[N*3],id[N],posl[N],posm[N],posr[N],f[N][N*3]; 7 bool cmp(int x,int y){ 8 return a[x]<a[y]; 9 } 10 int main(){ 11 scanf("%d",&n); 12 for(int i=1;i<=n;i++){ 13 scanf("%d%d",&a[i],&l[i]); 14 mat[a[i]]=mat[a[i]-l[i]]=mat[a[i]+l[i]]=1; 15 } 16 for(it=mat.begin();it!=mat.end();it++){ 17 mat[(*it).first]=++m; 18 pos[m]=(*it).first; 19 } 20 for(int i=1;i<=n;i++)id[i]=i; 21 sort(id+1,id+n+1,cmp); 22 for(int i=1;i<=n;i++){ 23 int x=id[i]; 24 posl[i]=mat[a[x]-l[x]]; 25 posm[i]=mat[a[x]]; 26 posr[i]=mat[a[x]+l[x]]; 27 } 28 for(int i=1;i<=n;i++) 29 for(int j=1;j<=m;j++){ 30 int x=id[i]; 31 f[i][j]=f[i-1][j]; 32 if (a[x]<pos[j])f[i][j]=max(f[i][j],f[i-1][posm[i]]+min(pos[j]-a[x],l[x])); 33 if ((a[x]-l[x]<pos[j])&&(pos[j]<=a[x]))f[i][j]=max(f[i][j],f[i-1][posl[i]]+(pos[j]-(a[x]-l[x]))); 34 if (a[x]<pos[j]){ 35 f[i][j]=max(f[i][j],f[i-1][posl[i]]+l[x]); 36 for(int k=1;k<i;k++)f[i][j]=max(f[i][j],f[k-1][posl[i]]+min(a[id[k]]+l[id[k]],pos[j])-(a[x]-l[x])); 37 } 38 } 39 printf("%d\n",f[n][m]); 40 return 0; 41 }