LG 7078 贪吃蛇
[CSP-S2020] 贪吃蛇
题面
题目描述
草原上有 \(3\leq n\leq 10^6\) 条蛇,编号分别为 \(1, 2, \ldots , n\) 。初始时每条蛇有一个体力值 \(a_i\) ,我们称编号为 \(x\) 的蛇实力比编号为 \(y\) 的蛇强当且仅当它们当前的体力值满足 \(a_x > a_y\) ,或者 \(a_x = a_y\) 且 \(x > y\) 。
接下来这些蛇将进行决斗,决斗将持续若干轮,每一轮实力最强的蛇拥有选择权,可以选择吃或者不吃掉实力最弱的蛇:
- 如果选择吃,那么实力最强的蛇的体力值将减去实力最弱的蛇的体力值,实力最弱的蛇被吃掉,退出接下来的决斗。之后开始下一轮决斗。
- 如果选择不吃,决斗立刻结束。
每条蛇希望在自己不被吃的前提下在决斗中尽可能多吃别的蛇(显然,蛇不会选择吃自己)。
现在假设每条蛇都足够聪明,请你求出决斗结束后会剩几条蛇。
本题有多组数据,对于第一组数据,每条蛇体力会全部由输入给出,之后的每一组数据,会相对于上一组的数据,修改一部分蛇的体力作为新的输入。
题解
首先要读题,搞清楚这个决斗到底是什么东西:
这是每轮 \(\max-\min\) 的连续过程, \(\max\) 玩家只能选择中止这个过程,而不能改变该过程。
决斗的过程就是不断 \(\max-\min\) 至一个元素之过程的前缀。
下一步观察在决斗结束时,活下来的蛇有什么性质,(看反面),注意到死去的蛇注定没有获得过主动权。(否则他会在之前弃权结束掉决斗)进一步分析什么时候第一条有过主动权的蛇成为最弱蛇(称该蛇为关键蛇),如没有这样的蛇,决斗在只有一个幸存者时结束。
在此之前,经历过决斗的蛇都不成为最弱蛇,故而每轮决斗的 \(\min\) 单增,以及每轮决斗的 \(\max\) 单减是显然的,于是每轮生成的 \(\max-\min\) 是单减的,进一步得到关键蛇恰在吃掉最弱蛇后成为最弱蛇。
现在决斗的结束只有两种可能了:关键蛇弃权而不成为最弱蛇,或者关键蛇确信决斗会在下一轮结束而吃掉最弱蛇。
(由于关键蛇必不会死去,决斗不能在此之后终止;又由此,决斗不会在此前结束)
这推出 \(\max-\min\) 不成为新的 \(\min\) ,则该轮必会进行。
接下来判定决斗是否在下一轮结束,只需要从下一轮为起始再次运用上述结论。但这次我们只关心起始轮会不会发生了:只有吃掉关键蛇的蛇因此成为最弱蛇,我们才需要递归地判定下去。
总结以下前述的分析,决斗将在 \(\max-\min\) 不成为新的 \(\min\) 时持续下去,判断第一个新 \(\min\) 是否会出现,其出现要求从它开始恰有连续偶数轮 \(\max-\min\) 成为新的 \(\min\) (如新的 \(\min\) 是最后一个数,不认为他是新 \(\min\) )
至此我们分析完了此题,只需要模拟决斗的过程并按上述方式进行判断即可。需要维护的是取 \(\max\) ,取 \(\min\) ,插入一个数;根据前述的 \(\max-\min\) 的单调性,可以用一个队列维护新产生的数,复杂度 \(\mathcal{O}(n)\)
多组数据有点小坑,只有 \(T\leq 10\) 组,实际上直接每一组单独做就可以,不必多想。
代码
//https://www.luogu.com.cn/problem/P7078
//20211103 nksbwen
#include <cstdio>
#include <cstring>
const int MAXN=1000111;
int T;
int N;
struct Pair{
int v, id;
};
bool operator < (const Pair &A, const Pair &B){
return (A.v==B.v)?A.id<B.id:A.v<B.v;
}
bool operator > (const Pair &A, const Pair &B){
return (A.v==B.v)?A.id>B.id:A.v>B.v;
}
Pair operator - (const Pair &A, const Pair &B){
return Pair{A.v-B.v, A.id};
}
Pair A[MAXN], B[MAXN], C[MAXN];
int Bl, Br, Cl, Cr;
int Deal(){
int Tag=0, Tmp;
while((Br-Bl+1)+(Cr-Cl+1)>1){
if(Cl>Cr || (Br>=Bl && B[Br]>C[Cl])){
//max=B[Br];
if(Cl>Cr || (Br>=Bl && B[Bl]<C[Cr])){
//min=B[Bl];
// printf("%d %d %d %d\n", B[Bl].v, B[Bl].id, B[Br].v, B[Br].id);
C[++Cr]=B[Br--]-B[Bl++];
}
else{
//min=C[Cr];
C[Cr]=B[Br--]-C[Cr];
}
}
else{
//max=C[Cl];
if(Cl>Cr || (Br>=Bl && B[Bl]<C[Cr])){
//min=B[Bl];
C[++Cr]=C[Cl++]-B[Bl++];
}
else{
//min=C[Cr];
C[Cr]=C[Cl++]-C[Cr];
}
}
if((Bl<=Br && C[Cr]>B[Bl]) || (Cl<Cr && C[Cr]>C[Cr-1]) || (Bl>Br && Cl==Cr)){
if(Tag) break;
else continue;
}
else ++Tag;
if(Tag==1) Tmp=(Br-Bl+1)+(Cr-Cl+1)+1;
}
return (Tag)?(Tmp-!(Tag&1)):1;
}
int main(){
scanf("%d", &T);
scanf("%d", &N);
for(int i=1;i<=N;++i){
scanf("%d", &A[i].v);
A[i].id=i;
}
memcpy(B+1, A+1, sizeof(Pair)*N);Bl=1;Br=N;Cl=1;Cr=0;
printf("%d\n", Deal());
for(int i=1, k;i<T;++i){
scanf("%d", &k);
for(int j=1, a, b;j<=k;++j){
scanf("%d%d", &a, &b);
A[a].v=b;
}
memcpy(B+1, A+1, sizeof(Pair)*N);Bl=1;Br=N;Cl=1;Cr=0;
printf("%d\n", Deal());
}
return 0;
}