CSP-S2020 贪吃蛇

参考博客:

https://www.luogu.com.cn/blog/EntropyIncreaser/ying-ye-ri-zhi-2020117-yi-ci-xin-xi-fou-dui-cheng-yin-fa-di-csp2020-t4-post

https://qaq-am.com/CSPS-2020-贪吃蛇/


\(n\)条蛇,执行若干次这样的操作:最强的蛇(以能力值为第一关键字,编号为第二关键字)有选择是否执行的权力,如果不执行就结束,如果执行就把最弱的蛇吃掉,并且自己的能力值变成两者之间的差。

假设所有的蛇都绝顶聪明,它们希望吃尽量多的蛇而且自己不被吃,问最终剩下多少蛇。

\(T\le 10,n\le 10^6\)

保证一开始给出的序列单调递增。


先讲一个读完题意就会的\(70\)分做法:由于每条蛇可以预知未来,所以它可以推演它选择吃之后的情况,推演出来如果它会死,那么就选择不吃以改变这种情况,否则选择吃。然后就直接模拟:先假设所有的蛇失去了理智,一直互相吃到最后,然后把这个情况倒推回来。到某一条蛇选择的时候,如果后面它死了,就改变未来,即将当前情况到它做出选择的这个区间的操作都撤销掉。

于是这样最终的问题是:维护一个序列,每次取出最大值和最小值,作差后再丢进去。这个东西用set可以随便做到\(O(n\lg n)\)

优化这个维护的过程到\(O(n)\),目前我只知道有EI的做法是对的(当然我没有看懂)。

现在讲讲LK的做法:

考虑如果一条蛇做出选择之后它不是最小值,那么它尽管吃,因为下一个做出选择的蛇一定比它死得更快(因为做差后能力值更小)。

如果它是最小值呢?那么这时候它就预知未来,让蛇们失去理智互吃,直到出现一条蛇做出选择之后不是最小值(或者没有蛇了),这条蛇一定是会吃的;根据这个过程进行的步数的奇偶性进行分类讨论,决定它是否吃,如果不吃就结束,如果吃也会在它下一条做出决定的蛇结束。

这样这道题就做完了。

这个做法妙就妙在:它没有把蛇们失去理智互吃的全过程搞出来,而是通过我们只关心最终的正确结果而设计的算法,在可以得到正确结果的时候return,避免了后面可能出现的问题。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 1000005
#define mp(x,y) make_pair(x,y)
int n;
pair<int,int> a[N],b[N];
int ah,at,bh,bt;
bool empty(){return !(ah<=at || bh<=bt);}
pair<int,int> gmin(){
	if (ah<=at && a[ah]<b[bt] || bh>bt)
		return a[ah];
	return b[bt];
}
pair<int,int> pop_front(){
	if (ah<=at && a[ah]<b[bt] || bh>bt)
		return a[ah++];
	return b[bt--];
}
pair<int,int> pop_back(){
	if (ah<=at && a[at]>b[bh] || bh>bt)
		return a[at--];
	return b[bh++];
}
pair<int,int> doit1(){
	pair<int,int> p=pop_front(),q=pop_back(),r;
//	printf("(%d,%d) eats (%d,%d)\n",q.first,q.second,p.first,p.second);
	r.first=q.first-p.first;
	r.second=q.second;
	return r;
}
void doit2(pair<int,int> &r){
	pair<int,int> q=pop_back();
	r.first=q.first-r.first;
	r.second=q.second;
}
int work(){
	int ans=n,i=1;
	ah=1,at=n;
	bh=1,bt=0;
	for (;i<n;++i){
		pair<int,int> r=doit1();
		if (r<gmin() && !empty()){
			int tmp=1;
			for (++i;i<n;++i){
				doit2(r);
				tmp++;
				if (r>gmin() && !empty()){
					if (tmp&1^1)
						return ans;
//					assert(0);这个assert是假的,因为可能会出现值相同编号不同的问题。
					return ans-1;
				}
			}
			if (tmp&1^1)
				return ans;
			return ans-1;
		}
		else{
			ans--;
			b[++bt]=r;
//			printf("(%d,%d)\n",r.first,r.second);
		}
	}
	return ans;
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	freopen("snakes.in","r",stdin);
	freopen("snakes.out","w",stdout);
	int T;
	scanf("%d%d",&T,&n);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i].first),a[i].second=i;
	printf("%d\n",work());
	T--;
	while (T--){
		int k;
		scanf("%d",&k);
		for (int i=1;i<=k;++i){
			int x,y;
			scanf("%d%d",&x,&y);
			a[x].first=y;
		}
		printf("%d\n",work());
	}
	return 0;	
}
posted @ 2020-11-10 19:46  jz_597  阅读(415)  评论(0编辑  收藏  举报