Luogu P4409:给出 n 个环形互斥集合
$A_i$ 的大小$|A_i|=a_i$ , 求$\min|\bigcup Ai|$ .
环形互斥:$A_1\cap A_2=A_2\cap A_3=...=A_{n-1}\cap A_n=A_n\cap A_1=\emptyset$ .
1 ≤ n ≤ 2e4,$a_i$ ≤ 1e5.
虽然这里给出了形式的描述, 还是建议看看原题面, 不然下面的说法可能有歧义.
在本文里设对于某种选法,
n 是奇数时上面的取法行不通. 尽管不知道有没有别的取法让 s 成为答案, 但我们知道如果答案比 s 小, 那肯定会有至少两个相邻集合的冲突, 一定不可以成为答案. s 是答案的可能下界.
假如某种选法里,
假设二分出一个大小 mi, 现在讲怎样验证能不能安排出总大小不超过 mi 的方案.
设
要想让
这时
因为二分下界
对于 A(i-1), 最多有 g(i-1) 在
剩余的 (a(i-1)-g(i-1)) 个都在外面 (按交集的定义, 这肯定不是负数).
然后因为
也就是
取完这些后再从 A1 里面取
这时就先让
AC code:
#include <iostream>
const int N=200'05, A=100'005;
int n, a[N], lf, mi, rg;
bool ch() {
int ff=a[0], gg=a[1], f, g;
for (int i=1; i<n; i++) {
// ff=f(i-1), gg=g(i-1), f=f[i], g=g[i]
f=std::max(0, a[i]-((mi-a[0]) - (a[i-1]-gg)));
g=std::min(a[i], a[0]-ff);
ff=f, gg=g;
}
return !ff; // ff 不为 0 的话意味着 An 和 A1 冲突了
}
int main() {
using std::cin;
cin >> n;
for (int i=0; i<n; i++) cin >> a[i], rg+=a[i];
if (n==1) return std::cout << a[0] << '\n', 0;
lf=a[0]+a[1];
if (n==2) return std::cout << lf << '\n', 0;
for (int i=1; i+1<n; i++)
lf=std::max(lf, a[i]+a[i+1]);
if (n%2==0) return std::cout << lf << '\n', 0;
// 保证 n>=3 && n 是奇数;
while (lf<rg) {
mi=lf+(rg-lf>>1);
if (ch()) rg=mi;
else lf=mi+1;
}
std::cout << rg << '\n';
}