题解:P16541 [EGOI 2026] 饼干 / Biscuits
lailai0916 · · 题解
题意简述
两人从栈顶分饼干。每回合 Aurora 先禁止一个拿取数量,Bianca 再选择另一个合法数量;Aurora 吃掉所选数量的饼干,若仍有剩余,Bianca 再吃掉下一块。求最优博弈下 Bianca 的总重量,并支持单点修改饼干重量。
解题思路
所有饼干最终都会被其中一人吃掉,总重量固定。Aurora 最大化自己的幸福度,等价于最小化 Bianca 的幸福度。
设当前剩余后缀为
若
Aurora 可以禁止任意一个合法的
从栈底向栈顶逐块加入饼干。设当前候选收益多重集的最大值和次大值分别为
原有候选收益仍然存在。因为
只维护两者之差:
加入重量
初始令
每次加入重量
Bianca 的答案是较小值
每块重量
初始
线段树按原数组顺序建立。对区间
代码使用迭代线段树的叶子布局。超过 unsigned char 存储。
建树时间复杂度为
正确性证明
对任意后缀,Bianca 的每个合法选择都唯一对应一个候选收益。Aurora 只能禁止一个选择,并希望 Bianca 在其余选择中的最大收益尽量小,所以结果恰为候选多重集的次大值。该结论同时覆盖最大值并列的情况。
从栈底向上加入重量
每个线段树叶子准确表示单块转移。父结点先应用右半段,再应用左半段,与从栈底到栈顶的实际处理顺序一致。根据区间长度归纳,根结点表示完整饼干栈的差值复合函数。修改后重算根链仍保持这一性质,所以每次输出都等于对应局面的最优 Bianca 幸福度。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N=100005;
const int M=262145;
const int V=50;
int n,q,s,sum,a[N];
unsigned char f[M][V+1];
void set_leaf(int u,int x)
{
for(int i=0;i<=V;i++)f[u][i]=abs(i-x);
}
void push_up(int u)
{
for(int i=0;i<=V;i++)f[u][i]=f[u<<1][f[u<<1|1][i]];
}
void update(int p)
{
int u=s+p;
set_leaf(u,a[p]);
while(u>1)
{
u>>=1;
push_up(u);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>q;
s=1;
while(s<n)s<<=1;
for(int i=0;i<s;i++)
{
if(i<n)
{
cin>>a[i];
sum+=a[i];
set_leaf(s+i,a[i]);
}
else
{
for(int j=0;j<=V;j++)f[s+i][j]=j;
}
}
for(int i=s-1;i;i--)push_up(i);
cout<<(sum-f[1][0])/2<<'\n';
while(q--)
{
int p,z;
cin>>p>>z;
sum+=z-a[p];
a[p]=z;
update(p);
cout<<(sum-f[1][0])/2<<'\n';
}
return 0;
}