洛谷 P7078 - [CSP-S2020] 贪吃蛇(贪心)

题面传送门

题意:

  • \(n\) 条蛇,每条蛇有个实力 \(a_i\)
  • 我们称编号为 \(x\) 的蛇比编号为 \(y\) 的蛇强,当且仅当 \(a_x>a_y\)\(a_x=a_y\)\(x>y\)
  • 每次实力最强的蛇可以选择吃掉实力最弱的蛇或者不吃,如果实力最强的蛇选择吃,那么它的实力会减去实力最弱的蛇的实力,实力最弱的蛇将消失。
  • 假设每条蛇都会选择最优策略(在保证自己不被吃的条件下吃掉尽可能多的别的蛇),问最后会剩下多少条蛇。
  • \(\sum n\le 10^7\)

薅洛谷题解 ing

首先我们来挖掘一些性质。可以发现,如果一条蛇吃完最弱的蛇之后不是最弱的蛇,那么它一定会选择吃。因为如果最强的蛇吃完最弱的蛇之后还是最强的蛇,那它不吃白不吃。否则,下一步最强的蛇的实力肯定是弱于原来最强的蛇的实力的,并且由于这条蛇吃完之后不是最弱的蛇,最弱的蛇的实力也强于原来最弱的蛇的实力,也就是说,下一步最强的蛇吃完最弱的蛇之后,肯定比当前最强的蛇吃完最弱的蛇之后的实力更菜,也就是说,如果下一步最强的蛇死了,那它死的时间肯定比当前这条蛇死的时间早,而下一步最强的蛇肯定会选择保住自己,因此下一步最强的蛇一定不会死,故就算这一步最强的蛇吃了最弱的蛇,它也不会死,因此它肯定会选择吃。

那么如果一条蛇吃了最弱的蛇之后变成了最弱的蛇之后怎么办呢?显然,如果一条蛇吃了最弱的蛇之后变成了最弱的蛇,而下一步最强的蛇吃了最弱的蛇之后不是最弱的蛇,或者下一步只剩两条蛇,那它肯定不会吃,因为如果它吃了最弱的蛇,那么下一步最强蛇可以安心吃掉最弱的蛇,它也就挂掉了。

我们再往前回溯一格,如果一条蛇吃了最弱的蛇后,下一步最强的蛇满足之前所述的状态,那么这条蛇会选择吃掉最弱的蛇,因为下一步最强蛇不敢吃最弱的蛇,否则它就会死。因此这一步最强的蛇可以放心大胆地吃,而下一步的蛇又不敢吃,因此这种情况总蛇数会少一。

如果再往前回溯一格那也可以得到类似的结论,如果最强蛇吃完最弱蛇之后回到了上一步所说的状态,那它又不敢吃了,因为吃了之后下一步最强蛇可以安心吃最弱蛇。

我们可以发现,出现最强蛇吃了最弱蛇的情况之后,答案会不会减少一,取决于当前局面到最强蛇能够安心吃掉最弱蛇经过的轮数的奇偶性,如果不算“第一次出现最强蛇吃了最弱蛇变成最弱蛇”这一轮,算上“最强蛇能够安心吃掉最弱蛇”这一轮之后,轮数是偶数,那么答案会减少一。因此我们考虑将整个过程分为两个部分:

  • 第一部分:最强蛇吃完最弱蛇之后都不是最弱蛇,放心大胆吃,答案减一
  • 第二部分:出现某个最强蛇吃完最弱蛇之后是最弱蛇:重复上面的过程直到出现一条最强蛇可以放心大胆地吃掉最弱蛇,根据第二部分持续的轮数判断答案是否减一。

直接 set 维护大概可以拿到 70 分的好成绩。考虑优化。我们建立两个 deque,分别称作 \(q_1,q_2\),维护现在没有吃过别的蛇,和现在已经吃过别的蛇的蛇的实力,实力从队首到队尾依次递减,然后考虑如下过程:

  • 第一部分:
    • 每次取出 \(q_1,q_2\) 队尾元素中的较强者作为最强蛇,以及 \(q_1\) 队首元素作为最弱蛇,由于这一部分中所有最强蛇吃完最弱蛇后,都不是最弱蛇,因此这一轮的最弱蛇肯定没有吃过别人,即,在队列 \(q_1\) 中。
    • 我们计算出最强蛇吃完最弱蛇的实力,如果小于现在 \(q_1\) 队首的实力就进入第二部分,否则将它塞入 \(q_2\) 队首。根据之前的推论,此时吃完最弱蛇的最强蛇的实力,肯定比上一轮吃完最弱蛇的最强蛇实力更菜。
  • 第二部分:
    • 我们直接取出 \(q_1,q_2\) 的队尾,由于最弱蛇就是上一轮中吃完最弱蛇的最强蛇,因此我们不用取出 \(q_1/q_2\) 的队首元素,而是直接取出上一次吃掉别人的蛇即可。
    • 还是计算出最强蛇吃完最弱蛇的实力,如果此时这条蛇的实力比当前最弱蛇的实力强就退出,根据第二部分轮数的奇偶性判断是否令答案减一。否则继续重复上一步的过程。

时间复杂度 \(\sum n\)

const int MAXN=1e6;
int n,a[MAXN+5];bool fst=0;
deque<pii> q1,q2;
pii getmx(){
	pii p;
	if(q1.empty()) return p=q2.back(),q2.ppb(),p;
	if(q2.empty()) return p=q1.back(),q1.ppb(),p;
	if(q2.back()>q1.back()) return p=q2.back(),q2.ppb(),p;
	return p=q1.back(),q1.ppb(),p;
}
void solve(){
	if(!fst){
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	} else {
		int c;scanf("%d",&c);
		while(c--){
			int x,y;scanf("%d%d",&x,&y);
			a[x]=y;
		}
	} fst=1;
	while(!q1.empty()) q1.ppb();
	while(!q2.empty()) q2.ppb();
	for(int i=1;i<=n;i++) q1.push_back(mp(a[i],i));
//	for(int i=1;i<=n;i++) printf("%d%c",a[i]," \n"[i==n]);
	while(1){
		if(q1.size()+q2.size()<=2) return puts("1"),void();
		pii p=getmx();
		int y=q1.front().fi;q1.pop_front();
		if(q1.empty()||mp(p.fi-y,p.se)<q1.front()){
			int cnt=0,res=q1.size()+q2.size()+1,pre=p.fi-y;
			while(1){
				cnt++;
				if(q1.size()+q2.size()<2){
					if(cnt&1) res++;
					printf("%d\n",res);
					return;
				} pii nwp=getmx();
				if((q1.empty()||mp(nwp.fi-pre,nwp.se)<q1.front())&&
				   (q2.empty()||mp(nwp.fi-pre,nwp.se)<q2.front()));
				else {
					if(cnt&1) res++;
					printf("%d\n",res);
					return;
				} pre=nwp.fi-pre;
			}
		} else q2.push_front(mp(p.fi-y,p.se));
	} assert(0);
}
int main(){
//	freopen("snakes4.in","r",stdin);
	int qu;scanf("%d",&qu);
	while(qu--) solve();
	return 0;
}
posted @ 2021-10-20 13:35  tzc_wk  阅读(189)  评论(0)    收藏  举报