[題解]ABC337E Bad Juice

Sinktank發表於2024-05-03

ABC337E Bad Juice

一開始的想法如下:

image

就是利用二分法,對於一個區間\([l,r]\),分成\([l,mid-1],[mid,r-1]\)兩部分,各找兩個朋友喝,右邊還空出一個\(r\),如果前面兩個朋友都沒中毒,那說明\(r\)這瓶有毒。

但仔細一想,我們發現\([1,n)\)的瓶子中任意一個我們分出的區間\([l,r]\),都用去了\(r-l+1\)個朋友。比如上圖中\([4,7]\)區間,就用了\(4\)個朋友。所以我們最終只是省下了\(1\)個朋友,用去了\(n-1\)個朋友。

這顯然不夠優,我們應該怎麼分配呢?

你可能以前玩過一個猜數字的遊戲:給你\(6\)張卡片,問你想的數在哪些卡片上。最終就可以猜出你想的數。

這個遊戲的原理就是第\(i\)張卡片上的數字,都是二進位制表示中第\(i\)\(1\)的數字。最終你選了哪些卡片,就說明你選的數二進位制表示中哪些位上是\(1\)

我們用同樣的原理,讓第\(i\)個朋友去喝二進位制表示中第\(i\)為是\(1\)的果汁。最終我們只需要用\(\lfloor \log_2(n-1)\rfloor+1\)個朋友。

之所以是\(n-1\),是因為我們可以把最後一瓶空出來,如果所有朋友都沒中毒,說明毒就下在最後一瓶裡。所以我們需要特判,如果最終結果為\(0\),則輸出\(n\)

點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
int n;
map<int,set<int>> ma;
string s;
int main(){
	cin>>n;
	int m=__lg(n-1)+1;
	cout<<m<<endl;
	for(int i=1;i<n;i++){
		int tmp=i;
		for(int j=1;tmp;j++){
			if(tmp&1) ma[j].insert(i);
			tmp>>=1;
		}
	}
	for(auto i:ma){
		cout<<i.second.size()<<" ";
		for(auto j:i.second) cout<<j<<" ";
		cout<<endl;
	}
	cin>>s;
	s=' '+s;
	int ans=0;
	for(int i=m;i>=1;i--){
		ans<<=1;
		if(s[i]=='1') ans|=1;
	}
	cout<<(ans==0?n:ans)<<endl;
	return 0;
}

相關文章