1. 程式人生 > 其它 >洛谷 P7078 - [CSP-S2020] 貪吃蛇(貪心)

洛谷 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;
}