其他分享
首页 > 其他分享> > [CSP2020-S] T4 贪吃蛇

[CSP2020-S] T4 贪吃蛇

作者:互联网

CSP2020-S T4 贪吃蛇

前言

​ 灵感源自老K与EI的题解,有所融会贯通。所有的蛇都足够聪明,就我挺笨的。

思路

​ 首先这道题,跟16年的蚯蚓很像,至少我在考场上面一看第一个想的就是双队列优化,但是这个暂时不能说是对的,也没错。都无妨。为了方便,设所有蛇的实力为 \(a_1,a_2,...,a_{n-1},a_n\).

​ 先来模拟一下吧。

​ 提出 \(a_n,a_1\) 作为最大值和最小值。这里建议想成合并蛇。合并成了一条 \(a_n-a_1\) 的蛇(记为 \(a_x\))。将它放入蛇群中。

​ 希望能够每次尽量快的取出最大值和最小值,则能双队列优化是最好的。

​ 现在目的是为了靠近单调性。

​ 分类讨论,

Case1:

​ 若是 \(a_x\) 仍为最小,则希望单调递减。则有柿子。

\[a_x>a_{n-1}-a_x\\ a_n-a_1>a_{n-1}-a_n+a_1\\ 2a_n-a_{n-1}>2a_1\\ a_n>2a_1 \]

​ 这个结论是 EI 所说过的。

Case2:

​ 若是 \(a_x\) 不为最小,则有 \(a_x>a_2\) 显然需要 \(a_x>a_{n-1}-a_2>a_{n-1}-a_x\) 成立。结论同上。所以只要每次取出的最大值为最小值的两倍以上就能保持单调。(此处有关等号的事情要求编号,无关紧要,手玩即可)

​ 于是由上文可知 “最大值减最小值不为新的最小值”(即 \(a_x>a_2\)),的情况下,可以使用双队列。

更进一步

​ 从现在开始,考虑的所有问题,都是回答当前最强蛇会不会吃

​ 考虑到在 \(case2\) 的情况下,这条蛇必吃无疑。

​ 证明:因为单调性,下一条最大的蛇(\(a_{n-1}\)) 去吃最小值之后一定会小于 \(a_x\) 。所以就算要死,也是 \(a_{n-1}\) 先死,而所有的吃过的蛇都是不可能死的。不然它会不吃转而直接结束游戏。所以 \(a_n\) 就不可能死。吃就完事了。

​ 所以连续的 \(Case 2\) 直接模拟就行。于是会遇到第一个 \(Case1\)

单调性的消亡

​ 很麻烦的是,\(Case1\) 的要求十分不好满足。所以当成冒着“风险”去吃,更进一步的说,是先吃,然后观察下一个最大的会不会吃。此时需要注意到这个问题依旧没有脱离“最强蛇会不会吃”的范畴,换句话说,有着递归性质

​ 那么递归的边界有两个。(即什么时候必吃)

第一个边界是其后的某一条蛇遇见了 \(Case2\) ,必吃。第二个边界是只剩两条蛇了。

​ 但是此时需要思考一个问题,即在 \(Case1\) 中,不满足单调性,那么一直从两个队列中取出最大值和最小值还是对的吗?答案是对的。而这也是我和机房同学思考了1个小时的问题。

​ 单调性是相对于多次的插入同时都在队列中而言的,但是可以发现,由于 \(Case1\) 的插入的 \(a_x\) 是最小的的性质,则必然会在下一次取出来。更具体的,当遇到一连串 \(Case1\) 的时候,就会一直在第二个队列中的某个位置上反复存储(若是中间有 \(Case2\) 就到达边界了)。

​ 同时最大值也肯定是对的,因为存储的一直只与最小值有关,除非只剩两条蛇,但这又恰好碰上了另一个边界。

​ 最大值合法,最小值也合法,不单调,也无妨。

奇偶性的影响

​ 这个都比较简单了,虽然说是递归,但是打的时候直觉就强烈的告诉我和奇偶性相关。

​ 不妨考虑边界做出决策的蛇是第 i 层 ,那么第 i-1 层必然不敢冒风险,于是第 i-2 层就能吃, i-3层不敢冒风险....

​ 所以性质显然,只需要记录递归多少层就行。

CODE

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<fstream>
#include<cmath>
using namespace std;
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
inline int read(){
	char ch=getchar();
	int res=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())res=(res<<3)+(res<<1)+(ch-'0');
	return res*f;
}
const int MAXN=1e6+5,inf=1e9;
int t,n,a[MAXN];
pii q1[MAXN],q2[MAXN];//q1单调递增,q2单调递减
int l1,r1,l2,r2;//首尾指针
inline pii mx(){//取出最大值且弹出
	if(r1==l1)return q2[l2++];
	else if(r2==l2)return q1[--r1];
	else if(q2[l2]>q1[r1-1])return q2[l2++];
	else return q1[--r1];
}
inline pii mn(){//取出最小值并弹出
	if(l1==r1)return q2[--r2];
	else if(r2==l2)return q1[l1++];
	else if(q2[r2-1]<q1[l1])return q2[--r2];
	else return q1[l1++];
}
inline pii M_min(pii x,pii y){
	return x<y?x:y;
}
inline void solve(){
	l1=r1=l2=r2=0;
	for(int i=1;i<=n;++i)q1[r1++]=mp(a[i],i);//初始化
	int fl=0,cnt=0,alf=0;
	while(1){
		++cnt;//计数器,同时也是死掉的蛇数
		pii x=mn(),y=mx();
		pii z=M_min((l1<r1?q1[l1]:mp(inf,-inf)),(l2<r2?q2[r2-1]:mp(inf,-inf)));//再取出一个最小值
		y.first-=x.first;
		if(y>z||cnt==n-1){//Case2和边界写一起了
			if(fl){
				printf("%d\n",n-(fl-(alf&1)));//奇偶性
				return ;
			}
			if(cnt==n-1){
				printf("1\n");
				return ;
			}
			q2[r2++]=y;//压入队列
		}
		else {
			alf++;//记录层数
			if(!fl)fl=cnt;	//第一个开始“冒风险”的蛇的之前死了多少
			q2[r2++]=y;//压入队列
		}
	}
}
int main(){
	t=read()-1;
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	solve();
	while(t--){
		int k=read();
		for(int i=1,x;i<=k;++i)x=read(),a[x]=read();
		solve();
	}
	return 0;
}

标签:return,队列,CSP2020,T4,最小值,贪吃蛇,Case1,最大值,单调
来源: https://www.cnblogs.com/clockwhite/p/13950942.html