题目描述见链接 .
当是一条链时, 答案显然为 , 但这是一个环, 需要考虑 与 的冲突关系 .
首先答案具有 单调性, 考虑二分答案, 二分出答案设为 , 然后是 部分 .
再设 与 的 最小集合交 大小为 , 我们的目标是使得 尽量小,
现在与 不相交的集合 大小为 ,
再考虑到 对 的影响, 可选且与不相交 的 集合大小 为 .
中的集合显然都应该加入到 中, 然后若还不够, 就只能使用与 冲突且不与 冲突的集合了, 这会增加 的值 .
所以 越大越好, 进而 越小越好, 再进而 越大越好,
为了达到这个目的, 设 表示 的最大值, 则通过类似上方的分析, 可以得到以下状态转移方程,
.
二分界限为 , 时间复杂度 .
有 个要注意的点
- 关于 与 的 要特殊处理 .
- 在检查 是否可行时, 需要先检查是否 .
#include<bits/stdc++.h>
#define reg register
const int maxn = 1e5 + 10;
int N;
int Ans;
int Max_A;
int A[maxn];
int min_inte[maxn];
int max_inte[maxn];
bool chk(int mid){
min_inte[1] = max_inte[1] = A[1];
for(reg int i = 1; i < N; i ++) if(A[i] + A[i+1] > mid) return false;
for(reg int i = 2; i <= N; i ++){
min_inte[i] = std::max(0, A[i] - (mid-A[i-1]-A[1]+max_inte[i-1]));
min_inte[i] = std::min(min_inte[i], A[i]);
max_inte[i] = std::min(A[1], A[1] - min_inte[i-1]);
max_inte[i] = std::min(max_inte[i], A[i]);
if(i == 2) min_inte[i] = max_inte[i] = 0;
// printf("%d: %d %d
", i, min_inte[i], max_inte[i]);
}
return !min_inte[N];
}
int main(){
scanf("%d", &N);
for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]), Max_A = std::max(Max_A, A[i]);
if(N == 1){ printf("%d
", A[1]); return 0; }
int l = 0, r = 3*Max_A;
Ans = 0x3f3f3f3f;
while(l <= r){
int mid = l+r >> 1;
if(chk(mid)) Ans = std::min(Ans, mid), r = mid - 1;
else l = mid + 1;
}
printf("%d
", Ans);
return 0;
}