CF713E [* easy]
有一个长度为 (m) 的环,最初有 (n) 个点上有人。
每秒可以让所有人都走一步(初始选定所有人的方向)
求使得所有点都被走过的最短时间。
(nle 10^5,mle 10^9)
Solution
我们发现答案的上界是最长的段。
考虑二分答案,然后 check
如果是序列的话,我们可以这样 dp,设 (f_i) 表示考虑到第 (i) 个点,
如果 (f_{i-1}<a_i),那么当前点必须往前走,否则往后走。
然而有一个问题,有可能是 (i-1) 帮忙覆盖了,然后 (i) 就只需要往前覆盖。
不难发现我们一定没有必要让 (i-2) 往后覆盖并以此使得 (i) 往前走,因为此时 (i-1) 一定可以往后走。
所以分类讨论一下即可。
然后我们发现环非常的不好处理。
但是最长的段一定是会被断开的。
所以枚举一下他两边的点是如何 choose 的,然后断环为链,你就 win 了,要注意如果他往右走,那么他的下一个可以往前走。
复杂度 (mathcal O(nlog m))
(Code:)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 2e5 + 5 ;
int n, m, a[N], b[N], dp[N], d, id ;
bool check(int x) {
rep( j, 0, 1 ) {
rep( i, 0, n ) dp[i] = -m ;
int Ed = m + b[1] - j * x, fs = Ed ; //j = 0 means after
dp[1] = b[1] + (j ^ 1) * x ;
if(!j) fs = m + b[2] - x - 1, dp[2] = max( b[2], b[1] + x ) ;
rep( i, (2 + (j ^ 1)), n ) {
if( dp[i - 2] >= b[i] - x - 1 ) dp[i] = b[i - 1] + x ;
if( dp[i - 1] >= b[i] - x - 1 ) dp[i] = max(dp[i], b[i]) ;
if( dp[i - 1] >= b[i] - 1 ) dp[i] = b[i] + x ;
}
if( dp[n] >= min( Ed - 1, fs ) ) return 1 ;
} return 0 ;
}
signed main()
{
m = gi(), n = gi() ;
rep( i, 1, n ) a[i] = gi(), a[i + n] = a[i] + m ;
if( n == 1 ) { printf("%d
", m - 1 ) ; exit(0) ; }
rep( i, 2, n + 1 ) {
int di = a[i] - a[i - 1] ;
if( d < di ) d = di, id = i ;
}
rep( i, 1, n ) b[i] = a[i + id - 1] ;
int l = 0, r = d, ans = d ;
while( l <= r ) {
int mid = (l + r) >> 1 ;
if(check(mid)) ans = mid, r = mid - 1 ;
else l = mid + 1 ;
}
printf("%d
", ans ) ;
return 0 ;
}