1. 程式人生 > 其它 >題解 P6874 [COCI2013-2014#6] KOCKICE

題解 P6874 [COCI2013-2014#6] KOCKICE

題目傳送門

演算法分析:二分

提供一種基於二分的演算法。

注意一下坑點

  • 最低那一列就是正中間那一列。
  • “最低的列左右兩側的磚頭數量必須相同”指的不是與中間列相鄰的兩列,而是所有對應的列

下面來分析一下如何二分。

這裡二分不直接二分答案。如果直接二分答案,並沒有把原問題變成更簡單的子問題。而二分的核心在於將複雜的原問題轉化為較簡單的子問題。因此,我們二分中間列的高度

單調性在哪裡?由於相鄰的磚頭高度恰相差 \(1\),中間列高度確定時其他列的磚頭數量也能確定。因此,若中間列高度繼續增加 \(1\) ,可以依此判斷出總操作次數的變化情況。假設當前中間列高度為 \(mid\),那麼第 \(i\)

列的目標高度為 \(x=mid+\left \vert n/2-i+1\right \vert\)。若中間列繼續增加 \(1\),那麼將有 \(res=\sum\limits_{i=1}^n (a_i \ge x)+(b_i \ge x)\) 列磚頭的操作次數將減 \(1\)\(2\times n-res\) 列磚頭操作次數將增加 \(1\)。隨著 \(mid\) 的增大,\(res\) 呈現出單調遞減的特徵,符合了二分的單調性要求。當 \(res \ge n\) 時,當前 \(mid\) 的是一個可行解。在求出中間列的最佳高度之後,暴力計算求出答案即可。

關於時間複雜度,檢查可行性為\(\mathcal{O}(n)\)

,因此二分時間複雜度為 \(\mathcal{O}(n\log_2(n))\)。最後計算答案為 \(\mathcal{O}(n)\)。因此總體時間複雜度為 \(\mathcal{O(n\log_2(n))}\)

下面給出程式碼:

#include<bits/stdc++.h>
#define ll long long
#define reg register
#define F(i,a,b) for(reg int i=a;i<=b;++i)
using namespace std;
inline ll read();
const int N=3e5+10;
int n,a[N],b[N];
ll ans,sum;
inline bool check(ll mid) {
	int res=0;
	F(i,1,n){
		ll x=mid+abs(n/2+1-i);
		res+=(a[i]>=x)+(b[i]>=x);//計算操作次數將-1的列數 
	}
	return res>=n;
}
int main() {
	n=read();
	ll l=0,r=0;
	F(i,1,n)a[i]=read(),r=max(r,(ll)a[i]);
	F(i,1,n)b[i]=read(),r=max(r,(ll)b[i]);
	while(l<=r){//二分 
		ll mid=l+r>>1ll;
		check(mid)?l=mid+1,ans=mid:r=mid-1;
	}
	F(i,1,n){//統計答案 
		ll x=ans+abs(n/2+1-i);
		sum+=abs(a[i]-x)+abs(b[i]-x);
	}
	printf("%lld",sum);
	return 0;
}
inline ll read() {
	reg ll x=0;
	reg char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))x=(x<<1ll)+(x<<3ll)+(c^48),c=getchar();
	return x;
}

AC

歡迎交流討論,請點個贊哦~