[CF559E] Gerald and Path

\(\text{Problem}:\)Gerald and Path

\(\text{Solution}:\)

首先将所有点按给定的端点排序,下面的线段标号为排序后的重标号。

\(f_{i,j,0/1}\)​ 表示当前考虑到第 \(i\)​ 个线段,右端点最右边的线段为 \(j\)​​​,此端点是否为给定的端点的最大覆盖长度。考虑向外转移,设枚举到第 \(k\)​ 个线段的方向为 \(t\)​,只需记录 \([i+1,k]\)​ 中所有可能右端点最大的线段 \(p\)​,从 \(f_{i,j,0/1}\)​ 转移到 \(f_{k,p,t}\)​,并加上 \(i,k,p\)​ 三条线段可能产生的新贡献即可。

乍一看这个转移漏洞重重,但实际上非常高妙。考虑为什么上述情况是可以得到最优转移的。

若存在线段 \(p\)​​ 的左右端点分别比 \(k\)​ 的左右端点小,但 \(p>k\)​​,那么关于 \(i,p,k\)​ 的最优转移会在 \(i\rightarrow p\)​ 时取到;反之同理。而只算 \(i,p,k\)​ 的新增贡献,忽视区间 \((i,k)\)​​ 中其他对答案也右贡献的线段的正确性,可以考虑画图举例,若还有其他对答案有贡献的线段,则会成为一个子问题被解决。​于是两个关于转移较大的疑点的正确性就得到了保证。

总时间复杂度 \(O(n^3)\)

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=110;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,f[N][N][2],ans;
struct Node { int x,l; }a[N];
inline bool cp(Node a,Node b) { return a.x==b.x?a.l<b.l:a.x<b.x; }
signed main()
{
	n=read();
	for(ri int i=1;i<=n;i++) a[i].x=read(), a[i].l=read();
	sort(a+1,a+1+n,cp);
	a[0].x=-1e9;
	for(ri int i=0;i<n;i++)
	{
		for(ri int j=0;j<=i;j++)
		{
			for(ri int k=0;k<2;k++)
			{
				int mx=-1e9,p,t;
				int nr=a[j].x+k*a[j].l;
				for(ri int x=i+1;x<=n;x++)
				{
					for(ri int y=0;y<2;y++)
					{
						int xr=a[x].x+y*a[x].l;
						if(mx<xr) mx=xr, p=x, t=y;
						f[x][p][t]=max(f[x][p][t],f[i][j][k]+mx-xr+min(xr-nr,a[x].l));
						ans=max(ans,f[x][p][t]);
					}
				}
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-08-10 17:45  zkdxl  阅读(27)  评论(0编辑  收藏  举报