题解 P7078 【贪吃蛇】
P7078 贪吃蛇
2020 CSP-S T4
考场上看到这道题想到了单调队列,但是没有分为两个单调队列优化,也没有考虑玩全下面的 $Part\;2$ ,导致大样例挂了,洛谷只有 $25\;pts$.
思路
我们设蛇为 $a_1,a_2,a_3,..,a_n$。
什么时候最强的蛇会吃最弱的蛇呢?
设蛇吃完后的实力为 $S=a_n-a_1$.
$$Part\;1$$ 如果这条蛇吃完后的实力 $S>a_2$,那么它会选择吃。
$$\because a_n> a_{n-k}\enspace and\enspace a_1< a_{1+k}$$$$\therefore S>a_{n-k}-a_{1+k}$$
即这条最强的蛇永远不会是最弱的一条,即使要被吃掉,也是 $a_{n-k}$ 先被吃,所以 $a_{n-k}$ 不会选择吃,所以这条蛇一定会吃。
从这里可以推出一条结论:吃过的蛇都不会被吃。
$$Part\;2$$
如果这条蛇吃完后的实力 $S<a_2$,则有以下情况:
-
$a_{n-1}-S>a_2$,如果 $a_{n-1}$ 吃了,它也不会变成最弱的蛇,也就永远不会被吃,所以 $a_{n-1}$ 会放心地吃,即 $a_n$ 会被吃掉,所以 $a_n$ 不会选择吃。
-
$a_{n-1}-S<a_2$,,如果 $a_{n-1}$ 吃了,则它会变成最弱的蛇,那么又需要考虑 $a_{n-2}$ 吃不吃了。
对于情况二,我们可以看为递归处理,递归边界为一条蛇必吃,而必吃的情况有:$1.$只剩两条蛇了;$2.$吃完后的实力 $S>a_2$。
递归了 $k$ 层,则
-
如果 $k$ 是奇数,即第 $k$ 条蛇一定会吃,所以第 $k-1$ 蛇不会吃,第 $k-2$ 蛇会吃,...所以第 $k-(k-1)=1$ 蛇会吃,所以剩余的蛇数为目前的蛇数 $-1$,则第 $2$ 条蛇不会吃,结束决斗;
-
如果 $k$ 是偶数,即第 $k$ 条蛇一定会吃,所以第 $k-1$ 蛇不会吃,第 $k-2$ 蛇会吃,...所以第 $k-(k-1)=1$ 蛇不会吃,所以剩余的蛇数为目前的蛇数,立即结束决斗。
大部分吃蛇都在 $Part\;1$ 发生,只有最后可能进行一次 $Part\;2$,判断能不能多吃一条。
$$Part\;3$$ 我们用两个单调队列 $a,b$ 维护,$a$ 用于维护没吃过的蛇,$b$ 用于维护吃过的蛇。上面已经证明过了,$b$ 队列中的蛇都不会被吃,所以每次吃掉的蛇都从 $a$ 队列首部取。
每个回合按顺序做一下操作:
-
如果目前只剩 $2$ 条蛇,就返回 $1$ 代表最后剩余 $1$ 条蛇。
-
从 $a$ 队列的前面取出并弹出最弱的蛇 $a_1$,从 $a$ 或 $b$ 队列后端取出并弹出最强的蛇 $a_n$ 。
-
如果 $a$ 队列空了,或者 $S<a_2$ ,进入 $Part\;2$。
-
否则,将这条蛇放在 $b$ 队列的首部(之前证明了后面吃完的蛇一定比之前吃完的蛇弱)。
代码实现
对于第 $1$ 个数据,我们单独读入,对于后面的 $T-1$ 组数据,以输入格式读入。
决斗中,我们可以使用 $STL$ 容器 $deque$ 或者数组模拟队列。
蛇的信息可以用结构体或者 $pair$ 保存(用结构体保存的话还需要重载运算符$<,>,-$)。
时间复杂度$O(Tn)$.
$AC\enspace code:$
#include<bits/stdc++.h>
using namespace std;
int t,n,k,len;
char ibuf[(1<<20)+1],*iS,*iT,out[(1<<20)+1];
#define gh() (iS==iT?iT=(iS=ibuf)+fread(ibuf,1,(1<<20)+1,stdin),(iS==iT?EOF:*iS++):*iS++)
#define reg register
inline int read(){//快读
reg char ch=gh();
reg int x=0;
while(ch<'0'||ch>'9') ch=gh();
while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=gh();
return x;
}
inline void write(int x){//快写
if(x>9) write(x/10);
out[len++]=x%10+48;
}
struct node{
int val,id;
friend bool operator<(const node &a,const node &b){//a比b弱
if(a.val!=b.val){
return a.val<b.val;
}
return a.id<b.id;
}
friend bool operator>(const node &a,const node &b){//a比b强
return b<a;
}
friend node operator-(const node &a,const node &b){//a吃b
return (node){a.val-b.val,a.id};
}
}s[1000001];
inline node zq(deque<node>&a,deque<node>&b){//取最强并弹出
node q;
if(b.empty()||!a.empty()&&a.back()>b.back()){
q=a.back();
a.pop_back();
}else{
q=b.back();
b.pop_back();
}
return q;
}
inline int solve(){//决斗
deque<node>a,b;
int ans;
for(int i=1;i<=n;i++){//原来的蛇
a.push_back(s[i]);
}
for(;;){
if(a.size()+b.size()==2){//剩余两条蛇
return 1;
}
node r=a.front();//最弱的
a.pop_front();//弹出
node q=zq(a,b);//最强的
node c=q-r;//S
if(a.empty()||a.front()>c){//S<a2
ans=a.size()+b.size()+2;//目前剩余的蛇
for(int eat=1;;eat++){//递归层数
if(a.size()+b.size()==1){//只剩1条蛇
return ans-!(eat&1);
}
q=zq(a,b);
c=q-c;
if(!a.empty()&&a.front()<c||!b.empty()&&b.front()<c){//一定吃
return ans-!(eat&1);
}
}
}else{//S>a2
b.push_front(c);//插入b队列
}
}
}
int main(){
// freopen("snakes.in","r",stdin);
// freopen("snakes.out","w",stdout);
t=read(),n=read();
for(int i=1;i<=n;i++){//读入原实力
s[i].val=read();
s[i].id=i;
}
write(solve());
out[len++]='\n';
for(int i=2;i<=t;i++){
k=read();
for(int j=1;j<=k;j++){
int u=read(),w=read();
s[u].val=w;//修改实力
}
write(solve());
out[len++]='\n';
}
fwrite(out,1,len,stdout);
return 0;
}
/*
Time:525ms
Memory:17.89MB
*/
再见了$qwq$~

浙公网安备 33010602011771号