洛谷P7078 [CSP-S2020] 贪吃蛇——题解

题目传送

大段参考OMG_wc的题解,前排致谢。

  十分简单的博弈论 + 一点观察数值关系的能力。

  首先容易想到,当前蛇王吃了老末后如果自己不再是蛇王了,就要看新蛇王的眼色(看在新蛇王的操作下自己是否还活着),而新蛇王吃蛇后如果不再是蛇王,它又要看新新蛇王的眼色。如此,一个简单的递归思路就好了。时间复杂度T n^2。

  观察一下数据性质,可知当前最强的蛇A吃了最弱的蛇之后,如果没有变成最弱的蛇,他一定会选择吃!因为在如果没有变成最弱的蛇的情况下,就算他不再是蛇王,新蛇王也没他原来强,新老末也不比原老末弱,那么新蛇王吃了后一定会比蛇A弱,而新蛇王会保证自己不死,那蛇A就一定不死了。

  故只需考虑蛇A吃了后变成最弱的蛇的情况,这时要看新蛇王B怎么打算,看B是否吃了后会变为最弱蛇,没有,则蛇A当蛇王时不能吃蛇,否则,B要看蛇C怎么吃。这样每条被看眼色的蛇若吃了后都会变成最弱的蛇,就得再看下一条蛇的眼色,这条递归的猜疑链终止于有一条蛇X可以放心吃了(吃了后不是最弱的蛇或吃了后只剩自己),那么X蛇之前的蛇就可推理知道自己该不该下嘴了。

  想到这后,就可把决斗过程分为以下几个阶段:

    1阶段:蛇王吃了后不会变为最弱蛇,尽情吃。

    2阶段: 蛇王Y吃了后变为最弱蛇,向后思考推理自己该不该吃,设向后考虑到第i条蛇时找到了第一条从蛇Y之后能放心吃老末的蛇X,若i为奇数,蛇王Y为了保命不能吃蛇;i为偶数,新蛇王为了保命不能吃蛇,蛇Y就能吃。

  可用set/平衡树维护这一队蛇的最值,时间复杂度Tnlogn,大数据会超时。

  仔细思考观察这些数值之间的性质,发现在阶段1中吃过蛇的原蛇王们吃了蛇后剩下的体力在不是最弱蛇的情况下是单调递减的,我们把他们拿出来单独放在一个单调递增的队列q2里,设一开始所有蛇都在队列q1里,则此时活着的最弱的蛇就在q1的队头(蛇被吃后就从队头弹出)里,也将会一直在q1队头,直至蛇王W吃蛇后变为最弱的蛇,就到了阶段2。

  到了阶段2,我们设个变量k记录当前最弱蛇的体力(一开始是W剩余的体力),此时q1、q2的蛇实力都不小于k,记录次数,依次取出两队列中最强的蛇吃掉最弱蛇……直至一条蛇吃掉最弱蛇后不是最弱蛇或只剩他一蛇了,就可根据记录的序号推理出蛇W该不该吃了。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 
  5 using namespace std;
  6 
  7 const int N=1e6+5;
  8 
  9 int n,tot,m1,m2,l;
 10 
 11 inline int read()
 12 {
 13     int x=0;
 14     char ch=getchar();
 15     while(!isdigit(ch))
 16         ch=getchar();
 17     while(isdigit(ch))
 18         x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
 19     return x;
 20 }
 21 
 22 struct member{
 23     int ord,val;
 24 }a[N],q[N];
 25 
 26 inline bool operator >(const member &a,const member &b)
 27 {
 28     return a.val>b.val||(a.val==b.val&&a.ord>b.ord);
 29 }
 30 
 31 inline member operator -(const member &a,const member &b)
 32 {
 33     member c;
 34     c.ord=a.ord;
 35     c.val=a.val-b.val;
 36     return c;
 37 }
 38 
 39 inline int memmax(int a,int b)
 40 {
 41     return q[a]>q[b]?a:b;
 42 }
 43 
 44 int noteat()
 45 {
 46     member now;
 47     int m;
 48     if(q[m1>l?m1:0]>q[m2<l?m2:0])
 49     {
 50         m=m1;--m1;
 51     }
 52     else
 53     {
 54         m=m2;++m2;
 55     }
 56     if(m==l)
 57         return 1;
 58     now=q[m]-q[l];
 59     if(q[(l+1)<=m1?l+1:1000002]>now&&q[l-1>=m2?l-1:1000002]>now)
 60     {
 61         q[l]=now;
 62         return !noteat();
 63     }
 64     else
 65     {
 66         return 0;
 67     }
 68 }
 69 
 70 inline void work()
 71 {
 72     for(int i=1;i<=n;++i)
 73         q[i]=a[i];
 74     tot=n;
 75     int m;
 76     m1=n,m2=l=1;
 77     member now;
 78     for(int i=1;i<=n;++i)
 79     {
 80         if(q[m1>l?m1:0]>q[m2<l?m2:0])
 81         {
 82             m=m1;--m1;
 83         }
 84         else
 85         {
 86             m=m2;++m2;
 87         }
 88         if(m==l)
 89             break;
 90         now=q[m]-q[l];
 91         if(q[(l+1)<=m1?l+1:1000002]>now&&q[l-1>=m2?l-1:1000002]>now)
 92         {
 93             q[l]=now;
 94             if(noteat())
 95                 --tot;
 96             break;
 97         }
 98         else
 99         {
100             q[l]=now;
101             --tot;
102         }
103         ++l;
104     }
105     printf("%d\n",tot);
106 }
107 
108 int main()
109 {
110     int t;
111     t=read()-1;
112     n=read();
113     for(int i=1;i<=n;++i)
114         a[i].val=read(),a[i].ord=i;
115     q[0].val=-1;q[1000002].val=2e9;
116     work();
117     int k,u,v;
118     while(t--)
119     {
120         k=read();
121         for(int i=1;i<=k;++i)
122         {
123             u=read(),v=read();
124             a[u].val=v;
125         }
126         work();
127     }
128     return 0;
129 }

 

posted @ 2021-08-17 19:40  千叶繁华  阅读(64)  评论(0编辑  收藏  举报