題解 貪吃蛇
一組可能可以 hack 掉你的代碼的數據:
input:
1
15
0 1 2 2 2 2 3 3 4 5 5 6 7 8 8
output:
8
題目分析
本篇題解參考了 EI 的一篇 blog ,建議大家去看看原文。
不難發現,操作序列是固定的,也就是說,如果吃蛇游戲進行了 \(i\) 輪,那么第 \(j(1\le j\le i)\) 輪的選擇是固定的,所以我們可以把問題分解為“求每一輪是哪條蛇吃掉了哪條蛇”和“已知每一輪的吃蛇情況,求出答案”。
首先考慮“已知每一輪的吃蛇情況,求出答案”,不妨假設第 \(i\) 輪的吃蛇情況是 \(A_i\) 吃掉了 \(B_i\) ,考慮從后往前遞推,由於每條蛇都不想被吃掉,所以如果在第 \(i\) 輪中, \(A_i\) 選擇吃掉 \(B_i\) ,那么必須要滿足在第 \(i+1\) 輪及以后中 \(A_i\) 都不會被吃掉,否則 \(A_i\) 肯定會直接結束游戲,用一個桶來統計就可以得到答案了。
然后考慮“求每一輪是哪條蛇吃掉了哪條蛇”,這個很容易用平衡樹或者堆等數據結構來維護,就是每次找出最大的和最小的,然后兩個相減再放回去,但是這樣的復雜度是 \(\mathcal O(Tn\log_2n)\) 的,不夠優秀。
不妨假設所有的蛇的實力從小到大實力一樣編號從小到大依次為 \(a_1,a_2,\dots,a_n\) ,如果 \(a_n\ge 2a_1\) ,那么 \(a_n-a_1\ge a_1\) ,此時最大值一定是不增的,最小值一定是不降的,可以用一個隊列來維護,每次都把最大值減去最小值加入到隊列中,不難發現,這個隊列一定是單調的,這個套路和 “NOIP2016蚯蚓” 是一樣的,我們使用一個隊列來維護答案,直到最大值小於最小值的兩倍。
此時 \(a_n<2a_1\) ,此時 \(a_1\ne 0\) ,可以得到每次取出的最大值減去最小值的值是多少:
- 第一次操作,最大值減去最小值為 \(a_n-a_1<a_1\) 。
- 第二次操作,最大值減去最小值為 \((a_{n-1}-a_n)+a_1\le a_1\) 。
- 第三次操作,最大值減去最小值為 \((a_{n-2}-a_{n-1})+a_n-a_1<a_1\) 。
- ......
需要注意的是,上面說的是“值”,但是實際比較大小的時候我們還需要比較編號。
不難發現,最小值最大也是 \(a_1\) ,所以我們可以使用一個數據結構來維護值為 \(a_1\) 的所有的編號,首先不妨假設 \(a_1=a_2=\cdots=a_m<a_{m+1}\le\cdots\le a_n\) ,那么在接下來 \(n-m\) 輪中,每一輪的最大值依次為 \(a_{n\sim m+1}\) 。
先考慮這 \(n-m\) 輪,最大值固定,如何維護最小值?可以使用類似 \(\mathcal O(n)\) 構造 prufer 序列的思路,用一個類似指針的東西來表示值為 \(a_1\) 的所有編號中最小的是什么,然后每次“最大值減去最小值”如果比這個指針的值要小,或者等於這個指針的值但是編號小於這個指針的編號的話,這個“最大值減最小值”就是下一次的最小值,下一次一定會被刪除,否則的話下一次的最小值就是這個指針指着的值,然后讓指針跳下一個就行了,不難發現,這部分的時間復雜度是 \(\mathcal O(n)\) 。
然后考慮后面 \(m\) 輪,此時剩下的只有兩種情況,一種情況是有一個值小於 \(a_1\) ,其他值都等於 \(a_1\) ,另一種情況是所有值都等於 \(a_1\) 。
如果有一個值小於 \(a_1\) ,不妨設為 \(x\) ,那么 \(x>0\) (因為 \(n-m\) 輪中的最后一輪的最大值為 \(a_{m+1}>a_1\) ,而每一輪的最小值都 \(\le a_1\) ),於是最小值就依次是 \(x,a_1-x,x,a_1-x,\dots\) ,所以一開始最大的蛇肯定會吃掉一開始小於 \(a_1\) 的蛇,后面的蛇就吃掉上一次吃東西的蛇。
如果所有值都等於 \(a_1\) ,那么一開始最大的蛇肯定會吃掉次小的蛇,然后實力變成 \(0\) ,然后被次次小的蛇吃掉,然后次次小的蛇變成最大的蛇,一直繼續下去。
這部分的時間復雜度也是 \(\mathcal O(n)\) 。
綜上,總的時間復雜度為 \(\mathcal O(Tn)\) 。
參考代碼
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;
template<typename T>void read(T&x){
static int f;static char c;
for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
static int q[64];int cnt=0;
if(x==0)return pc('0'),void();
if(x<0)pc('-'),x=-x;
while(x)q[cnt++]=x%10,x/=10;
while(cnt--)pc(q[cnt]+'0');
}
const int maxn=1000005;
int n,a[maxn],q0[maxn],q1[maxn],q2[maxn],A[maxn],B[maxn],in[maxn],cp[maxn],c2;
int hp[maxn];
void solve2(int st){
for(int i=1;i<=n;++i)hp[i]=false;
int key=a[q2[0]],p=1;hp[q2[0]]=true;
while(p<c2&&a[q2[p]]==key)hp[q2[p]]=true,++p;
int e=p,MN=0;p=1;while(!hp[p])++p;
for(int i=c2-1;i>=e;--i){
A[st]=q2[i];
if(MN){
B[st]=MN;
if(((a[q2[i]]-=a[MN])<key)||q2[i]<p)
MN=q2[i];
else{
MN=0;hp[q2[i]]=true;
}
}
else{
B[st]=p;hp[p]=false;++p;
while(p<=n&&!hp[p])++p;
a[MN=q2[i]]-=key;
}
++st;
}
if(MN&&a[MN]==key){
hp[p=MN]=true;MN=0;
}
if(MN){
for(int i=n;i>=1;--i)if(hp[i]){
A[st]=i;B[st]=MN;++st;MN=i;
}
}
else{
int o=0;
for(int i=n;i>=1;--i)if(hp[i]){
o^=1;
if(o){
if(MN){
A[st]=i,B[st]=MN;++st;
}
MN=i;
}
else{
A[st]=MN;B[st]=i;++st;
}
}
}
}
void solve(void){
for(int i=1;i<=n;++i)cp[i]=a[i],in[i]=0;
int f0=0,b0=0,f1=n,b1=n;
for(int i=1;i<=n;++i)q0[b0++]=i;
for(int i=1;i<n;++i){
int mx=-1,mxp=0;
if(f0<b0&&(a[q0[b0-1]]>mx||(a[q0[b0-1]]==mx&&q0[b0-1]>mxp)))mx=a[mxp=q0[b0-1]];
if(f1<b1&&(a[q1[b1-1]]>mx||(a[q1[b1-1]]==mx&&q1[b1-1]>mxp)))mx=a[mxp=q1[b1-1]];
int mn=1000000001,mnp=n+1;
if(f0<b0&&(a[q0[f0]]<mn||(a[q0[f0]]==mn&&q0[f0]<mnp)))mn=a[mnp=q0[f0]];
if(f1<b1&&(a[q1[f1]]<mn||(a[q1[f1]]==mn&&q1[f1]<mnp)))mn=a[mnp=q1[f1]];
if(a[mxp]<a[mnp]*2){
int _l=f0,_r=f1;c2=0;
while(_l<b0||_r<b1){
if(_r>=b1||(_l<b0&&(a[q0[_l]]<a[q1[_r]]||(a[q0[_l]]==a[q1[_r]]&&q0[_l]<q1[_r])))){
q2[c2++]=q0[_l++];
}
else{
q2[c2++]=q1[_r++];
}
}
solve2(i);
break;
}
A[i]=mxp;B[i]=mnp;
if(f0<b0&&q0[f0]==mnp)++f0;else ++f1;
if(f0<b0&&q0[b0-1]==mxp)--b0;else --b1;
a[mxp]-=a[mnp];q1[--f1]=mxp;
}
int no=n;
for(int i=n-1;i>=1;--i){
++in[B[i]];
if(in[A[i]])
while(no>i)--in[B[--no]];
}
--no;
for(int i=1;i<=n;++i)a[i]=cp[i];
write(n-no),pc('\n');
}
int main(){
int T;read(T),read(n);
for(int i=1;i<=n;++i)read(a[i]);
solve();--T;
while(T--){
int k;read(k);
while(k--){
int x,y;
read(x),read(y);
a[x]=y;
}
solve();
}
return 0;
}