【AtCoder】【模型轉化】【二分答案】Median Pyramid Hard(AGC006)

題意:

給你一個排列,有2*n-1個元素,現在進行以下的操作:
每一次將a[i]替換成爲a[i-1],a[i],a[i+1]三個數的中位數,並且所有的操作是同時進行的,也就是說這一次用於計算的a[],是這一次計算之前的那個a[]。每一次不操作開頭和結尾的兩個位置。這樣子每一次都會減少2個元素,問你最後剩下的元素是什麼。

數據範圍:

1<=N<=10^5

思路:

看見這道題正解是二分的時候,簡直震驚!(考試的時候一直想的是計算每一項在最終序列中的係數來做)。
我們可以二分出一個值x,將所有小於等於x的數都變爲0,將所有的大於x的數都變爲1,然後再來看待這個問題。
下面建議讀者一邊畫圖一邊來看。
首先定義一種狀態,叫做柱子。也就是當存在連續的兩個相同的數字的時候,這兩個數字的上方會連續出現兩個一樣的數字,直到成爲最左邊或者是最右邊的數字而被刪除爲止。
我們考慮哪些狀態下,頂層的數字會是0。假如說這個問題解決了,就可以二分了。
1.頂端的正下方就有一根值爲0的柱子,這個時候根據柱子的定義是顯然成立的;
2.頂端的左邊或者是右邊有一根值爲0的柱子,而另一邊沒有柱子。如果你畫一下圖就會發現,對於沒有柱子的區域而言,那麼必定是0101010…交錯排列的。那麼當前的這根0的柱子就能夠保證到達某一層之後,右邊的端點一定是0。這個時候就可以劃歸到第5種情況了;
3.頂端的兩端都有柱子,且都是0的柱子。如果說狀態2成立的話,那麼這個也是顯然成立的;
4.頂端的兩端都有柱子,且一邊最靠近頂端的柱子值爲0,令一端最靠近頂端的柱子值爲1,並且要求0柱子更加靠近1柱子。這個比較好想。到達某一層之後,1的柱子消失了,而這個時候0柱子至少還存在1個,且存在1個的時候還是再端右端點上。那麼仍然能夠劃歸到情況5;
5.頂端的兩端都沒有柱子,且底層的兩端都是0,也就是num0=num1+1。如果稍微畫一下圖你就能發現,這樣子0101010交錯着上去,每一層都是這樣子交錯的,且都是兩端爲0。那麼這樣子到了次頂端的時候必定是010,然後頂端就是0了。

這樣子對於一個01的序列直接O(n)判斷就好了,總時間爲O(nlogn)。

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100000
using namespace std;
int N,a[2*MAXN+5];
bool seq[2*MAXN+5];
int Check(int x)
{
	for(int i=1;i<=2*N-1;i++)
		if(a[i]<=x)
			seq[i]=0;
		else
			seq[i]=1;
	if(seq[N]==seq[N-1]||seq[N]==seq[N+1])
		return seq[N];//判斷頂端的正下方有沒有柱子
	int l=-1,r=-1;
	int ld,rd;
	for(int i=2;i<=N;i++)
		if(seq[i]==seq[i-1])
			l=seq[i],ld=N-i+1;//計算左邊的柱子類型以及距離
	for(int i=N;i<2*N-1;i++)
		if(seq[i]==seq[i+1])
		{
			r=seq[i];//計算右邊的柱子類型以及距離
			rd=i-N+1;
			break;
		}
	if((l==0&&r==-1)||(l==-1&&r==0)||(l==0&&r==0))//情況2和3
		return 0;
	if((l==1&&r==0&&ld>rd)||(l==0&&r==1&&ld<rd))//情況4
		return 0;
	if(l==-1&&r==-1&&seq[1]==0)//情況5
		return 0;
	return 1;
}
int main()
{
//	freopen("mid.in","r",stdin);
//	freopen("mid.out","w",stdout);
	scanf("%d",&N);
	for(int i=1;i<=2*N-1;i++)
		scanf("%d",&a[i]);
	int L=0,R=2*N;
	while(L<R)
	{
		int mid=(L+R)/2;
		if(Check(mid)==0)//O(n)判斷
			R=mid;
		else
			L=mid+1;
	}
	printf("%d\n",R);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章