[CF559E]Gerald and Path

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF559E]Gerald and Path相关的知识,希望对你有一定的参考价值。

Gerald and Path

题解

这道问题应该很容易被联想到 d p dp dp上,但真正的难点是我们怎样去设计我们的 d p dp dp
首先,我们发现我们真正重要的状态有两个,一是现在枚举了哪些边,一是我们现在的覆盖状态。
但显然我们是不可能将它们都记录下来的,这显然不大可能,那我的转移的要求相当于是我们可以通过我们记录的状态得到的信息之外的信息不可能对我们现在的转移造成影响。
那就意味着我们需要观察我们的合法解,从中找到一些性质。

这里我们可以发现,我们最后覆盖完后可以得到许多个线段的连通块,而在每个连通块中,所有线段的点 a a a,也就是给定的端点的位置在它给的域中是连续的。
那么我们显然可以按照端点排序去加入这些线段。
那么可以发现,我们每次加入的线段有两种可能的朝向,朝右或朝左,朝右的显然只需要计算它超出右端点的距离,对于这个我们可以记录下右端点。
而朝左就比较麻烦了,它可能会跨越几个连通块,将它们连起来,我们显然不可能把联通块的位置都计入状态。
但我在上面是说过的,我们可以将它拆成许多的连通块,所以我们没必要一次只加入一个线段,可以加入一个联通块呀。
但如果直接加联通块的话未必也太麻烦了,我们关注到之所以会有上面状况的原因是由于我们的某个向左扩展的线段直接跨越了向右扩展的线段的端点,到前面去了。
那么我们不妨在加入我们向右扩展的线段时就考虑将后面跨越它向左扩展的远的一个线段也加入进去。
它们两个的区间内所有线段形成的覆盖事实上只有两条线段是真正有意义的,一是向左最远的,一是向右最远的,显然,这两条线段之外的其它线段是没有意义的,我们可以考虑给它们随便定个方向,或者直接忽略掉。
当然,我们转移的时候是将最左侧的线段与最右边的线段当作有意义的线段,但它们会使之被忽略的线段却并不只有它们两个,毕竟向左与向右的线段都可以跨越对方,将它们区间外的某些线段也给忽略掉。
所以转移时我们还需要考虑我们加入的线段根本没有意义,被忽略掉了的情况。

事实上,我们可以定义 d p i , j , 0 / 1 dp_i,j,0/1 dpi,j,0/1,表示我们已经计入了前 i i i条线段,且现在的右端点是由我们第 j j j条线段的左端点或者右端点构成的。
转移也就是我们上面所说的几种,转移的是时间复杂度是 O ( n ) O\\left(n\\right) O(n),毕竟要枚举一直忽略到哪个点。

总时间复杂度 O ( n 3 ) O\\left(n^3\\right) O(n3)
据说EI还有一个 O ( n 2 ) O\\left(n^2\\right) O(n2)的做法…

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double Ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int lim=1500;
const int M=100000;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-3;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0')if(s=='-')f=-1;s=getchar();
	while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
	x*=f;

template<typename _T>
void print(_T x)if(x<0)x=(~x)+1;putchar('-');if(x>9)print(x/10);putchar(x%10+'0');
int gcd(int a,int b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;return t;
int n,dp[105][105][2],ans;
struct mingint pos,len;s[105];
bool cmp(ming x,ming y)return x.pos<y.pos;
signed main()
	read(n);
	for(int i=1;i<=n;i++)
		read(s[i].pos),read(s[i].len);
	sort(s+1,s+n+1,cmp);
	dp[0][0][0]=0;s[0].pos=-INF;
	for(int i=1;i<=n;i++)
		for(int j=0;j<i;j++)
			for(int k=0;k<2;k++)
				int lp=s[j].pos+k*s[j].len;dp[i][j][k]=max(dp[i-1][j][k],dp[i][j][k]);
				if(s[i].pos>=lp)dp[i][i][0]=max(dp[i][i][0],dp[i-1][j][k]+min(s[i].pos-lp,s[i].len));
				int rp=s[i].pos+s[i].len,ld=s[i].pos;if(rp<=lp)continue;
				for(int l=i;l<=n;l++)
					if(l>i)ld=min(ld,s[l].pos-s[l].len);
					if(s[l].pos-s[l].len>rp)continue;rp=max(rp,s[l].pos);
					if(rp>s[l].pos)dp[l][i][1]=max(dp[l][i][1],dp[i-1][j][k]+rp-max(ld,lp));
					else dp[l][l][0]=max(dp[l][l][0],dp[i-1][j][k]+s[l].pos-max(ld,lp));
				
			
	for(int i=0;i<=n;i++)
		for(int j=0;j<=i;j++)
			for(int k=0;k<2;k++)
				ans=max(ans,dp[i][j][k]);
	printf("%d\\n",ans);
	return 0;

谢谢!!!

以上是关于[CF559E]Gerald and Path的主要内容,如果未能解决你的问题,请参考以下文章

CF559C Gerald and Giant Chess

CF 559C - Gerald and Giant Chess (组合计数)

题解 CF559C Gerald and Giant Chess

CF559C Gerald and Giant Chess

Gerald and Giant Chess

打CF,学算法——二星级Codeforces Round #313 (Div. 2) B. Gerald is into Art(水题)