Codeforces 713E. Sonya Partymakert 题解

Codeforces 713E. Sonya Partymaker

Link!

题意

给出\(n\)个人在环上的位置,环长为\(m\),可以给每个人钦点一个方向,每个人每秒钟沿着钦定方向走一步,问全环都被人走过的最小时间。

题解

先二分时间\(x\),考虑如何判断当前时间是否合法。

考虑破环为链,最后合并答案。

\(f[i]\)表示前\(i\)个人最远能覆盖到哪里,\(p[i]\)表示第\(i\)个人的位置。

分当前的向左还是向右讨论。

还要考虑一种特殊情况为第\(i-1,i\)之间的空由\(i+1\)来向左填,让\(i\)向右扩展,\(f[i]\)\(f[i-2]\)转移即可。

为什么不会有\(i-1,i\)之间的空由\(i+2\)来向左填的情况?

答:如果\(p[i+2] - x < p[i]\)(即\(i+2\)能够覆盖到\(i-1,i\)之间的空),那么有\(p[i] + x > p[i+2]\),于是\(i,i+2\)之间的空已经被\(i\)覆盖,将向左的\(i+2\)改为\(i+1\)向左,\(i+2\)向右一定更优。

考虑合并时\(1\)向左可能覆盖到\(n\),会干扰状态转移,于是选取最长的一段空破环为链,取空的长度为二分上界,于是\(1,n\)之间不会互相覆盖。注意讨论\(1\)向左或向右,做两次\(dp\)即可。

难点总结

\(dp\)转移并非很难,难点在于观察到只会有\(i+1\)回头填\(i-1,i\)之间的空,以及选取最长的区间破环为链,避免干扰转移。

Code

#include<bits/stdc++.h>
#define ll long long
#define N 200015
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lowbit(i) ((i)&(-i))
#define VI vector<int>
#define all(x) x.begin(),x.end()
#define SZ(x) ((int)x.size())
using namespace std;
int n,m,a[N],p[N],f[N];
bool check(int x){
	f[1] = 0;
	// < - 1
	rep(i,2,n){
		f[i] = f[i-1];
		if(f[i-1] >= p[i] - 1) f[i] = max(f[i],p[i] + x);
		if(f[i-1] >= p[i] - x - 1) f[i] = max(f[i],p[i]);
		if(i > 2 && f[i-2] >= p[i] - x - 1) f[i] = max(f[i],p[i-1] + x);
	}
	if(f[n] >= m - 1 - x) return 1;
	// 1 - >
	if(p[1] + x < p[2] - x) return 0;
	f[2] = max(p[1] + x,p[2]);
	rep(i,3,n){
		f[i] = f[i-1];
		if(f[i-1] >= p[i] - 1) f[i] = max(f[i],p[i] + x);
		if(f[i-1] >= p[i] - x - 1) f[i] = max(f[i],p[i]);
		if(i > 2 && f[i-2] >= p[i] - x - 1) f[i] = max(f[i],p[i-1] + x);
	}
	if(f[n] >= m - 1 - max(x - p[2],0)) return 1;
	return 0;
}
int main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%d%d",&m,&n);
	rep(i,1,n) scanf("%d",&a[i]),a[i+n] = a[i] + m;
	sort(a+1,a+n+n+1);
	if(n == 1){
		printf("%d\n",m-1);
		return 0;
	}
	int pos = 2;
	int l = 0,r;
	rep(i,2,n+1) if(a[i] - a[i-1] > a[pos] - a[pos-1]) pos = i;
	r = a[pos] - a[pos-1] - 1;
	rep(i,1,n) p[i] = a[i+pos-1];
	int st = p[1];
	rep(i,1,n) p[i] -= st;
	int ans = 114514;
	while(l <= r){
		int mid = (l+r)>>1;
		if(check(mid)) ans = mid,r = mid-1;
		else l = mid+1;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-05-25 20:35  趁着胆子小  阅读(115)  评论(3编辑  收藏  举报
//explotion effect (unabled)