CF559E Gerald and Path

题面

\(n\) 条线段,每条线段给定其中一端的位置及长度,求所有线段覆盖的最大长度。
\(n \le 100\)

题解

首先按端点位置升序排序,不难想出状态 \(f_{i,j,op}\) 表示考虑前 \(i\) 个点,定向后右端点最靠右的是 \(j\) 号点的线段,且定向方向为 \(op\) ( \(0\) 向左, \(1\) 向右) 的最大长度

\(R(pos,op)\) 表示点 \(pos\) 的线段定向为 \(op\) 的右端点

要从 \(f_{i,j,op}\) 转移到 \(f_{i+1,r,d}\) ,若 \(i+1\) 往右,则 \(f_{i+1,r,d} = f_{i,j,op} + min(len_{i+1},R(i+1,1)-R(j,op))\)

但若要往左,我们会发现很不好办,因为可能 覆盖了之前两个线段的空隙 ,而这个不好计算,我们只能设计状态为 \(R(j,op)\) 左边的区间都不再让贡献。此时发现, \(i+1\) 包含了靠右的那条线段,启发我们 从线段的四种关系来分析 。我们首先只能对 \(j\) 进行分析,然后推广出更普遍的方法。

  1. \(R(j,op)\le R(i+1,op1)\)\(L(j,op)\le L(i+1,op1)\) ,则就是上面的转移式

  2. \(R(j,op)\ge R(i+1,op1)\)\(L(j,op)\ge L(i+1,op1)\) ,则 \(min(len_{i+1},R(i+1,1)-R(j,op)\) 为负,相当于把右端点移到了 \(R(i+1,op1)\) ,再加上 \(R(r,d)-R(i+1,op1)\) (因为 \(r\)\(i+1\) 左边,所以后面那段一定连续)。

  3. \((j,op)\) 包含 \((i+1,op1)\) ,那么也是第二类情况的转移式

我们发现上面两个转移式其实可以合并,就是

\(f_{i+1,r,d} = f_{i,j,op} + min(len_{i+1},R(i+1,1)-R(j,op)) + R(r,d)-R(i+1,op1)\)

  1. \((i+1,op1)\) 包含 \((j,op)\) ,那么 \((j,op)\) 没有贡献,可以直接忽略它,从前面的状态转移过来,转移式也是上面那个。而这个忽略显然可以往前推,即状态前推后可以把新的 \((j,op)\) 忽略掉,所以每次都是之前有过的状态。且忽略多了后答案会变小,所以直接对于所有 \(R(j,op)\le R(i+1,op1)\) 的状态都统计一下就好。

所以 \(R(j,op)\le R(i+1,op1)\)\(R(j,op)\ge R(i+1,op1)\) 的转移式是一样的!

因为我们必须保证 \((j,op)\) 确实是最靠右的线段,所以采取贡献式,\(k\) 不断右移并且将忽略的线段贪心地向右定向,并维护 \(r,d\)

\(O(N^3)\)

Code

#include<bits/stdc++.h>
#define ll long long
#define ri register int
using namespace std;
const int maxn = 110;
ll f[maxn][maxn][2];
int n;
struct range{
	int a,len; friend bool operator < (range x,range y){return x.a < y.a;}
}ran[maxn];
inline int rd(){
    int res=0,f=0;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=1;
    for(;isdigit(ch);ch=getchar()) res=(res<<3)+(res<<1)+ch-48;
    return f?-res:res;
}
inline int R(int pos,int op){
	if(!pos) return -0x3f3f3f3f;
	return op ? ran[pos].a + ran[pos].len : ran[pos].a;
}
int main(){
	n = rd(); for(ri i = 1;i <= n;++i) ran[i].a = rd(),ran[i].len = rd();
	sort(ran + 1,ran + n + 1);
	memset(f,-1,sizeof(f));
	f[0][0][0] = f[0][0][1] = 0;
	for(ri i = 0;i < n;++i)
	    for(ri j = 0;j <= i;++j)
		for(ri op = 0;op <= 1;++op)
	 	    if(f[i][j][op] != -1){
			int r = j,d = op;

			for(ri k = i + 1;k <= n;++k){
   			    if(R(k,0) >= R(r,d)) r = k,d = 0;
				f[k][r][d] = max(f[k][r][d],f[i][j][op] + min(ran[k].len,R(k,0) - R(j,op)) + R(r,d) - R(k,0));
				if(R(k,1) >= R(r,d)) r = k,d = 1;
				f[k][r][d] = max(f[k][r][d],f[i][j][op] + min(ran[k].len,R(k,1) - R(j,op)) + R(r,d) - R(k,1));
			}
 		    }
	ll ans = f[n][n][1];
	for(ri i = 1;i <= n;++i) for(ri j = 0;j <= 1;++j) ans = max(ans,f[n][i][j]);
	printf("%lld\n",ans);
    return 0;
}
posted @ 2022-02-05 14:07  Lumos壹玖贰壹  阅读(34)  评论(0编辑  收藏  举报