题解 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$~

posted @ 2020-11-15 12:13  ffffyc  阅读(33)  评论(0)    收藏  举报  来源