1. 程式人生 > 實用技巧 >題解 CF255D 【Mr. Bender and Square】

題解 CF255D 【Mr. Bender and Square】

圖掛了,再交一遍。

在部落格食用
在洛谷檢視題目 | 在CF檢視題目

翻譯

給出一個 \(n\times n\) 的正方形和一個點的座標(\(x,y\)),從這個點每秒可以向外擴散四個點,即 \((x+1,y),(x-1,y),(x,y+1),(x,y-1)\) ,求需要多少秒才能大於或等於面積 \(c\)

思路

如果將這個點看作是在無限大的正方形上擴散,那麼我們可以得出:

擴散時間: 0 1 2

點的數量: 1 5 13

於是,為了得出擴散時間和點的數量的關係,設一個二次函式為 \(y=ax^2+bx+c\) 。( \(x\) 為擴散時間, \(y\) 為點的數量)
將上表的資料帶入二次函式就得到了一個三元一次方程,圖片如下:

\(\therefore\) 這個二次函式為: \(y=2x^2+2x+1\)

現在我們開始考慮邊緣阻擋了的點的數量。
不難發現,在邊緣以外需要減去的點組成了一個像金字塔一樣的三角形(第一層有 \(1\) 個,第二層有 \(3\) 個 ……),圖片如下(手畫很醜,請見諒):

這個圖片的擴散時間為 \(3\) ,藍色區域代表點,紅色邊框在 \(3\times 3\) 的正方形邊緣外,綠色圓圈圈出的三角形即為突出部分。
提供一下小學奧數的知識:
\(1+3+5+...+2n-1=n^2\)
因此,就可以得出突出部分的點數,其他幾個邊也一樣。

然後,你會發現突出部分有重疊,如下圖綠圈部分:

多次畫圖後,發現:重疊部分是個每層點數相差1的三角形,於是就可以用高斯求和 \((1+2+3+...+n=\frac{(1+n)n}{2})\)

算出重疊部分點數。
所以總的點數=忽略邊緣時的點數-四邊突出的點數+重疊部分的點數
注:+重疊部分的點數是根據容斥原理

有了公式,就可以二分查詢答案了!

程式碼

注:二分查詢注意邊界!


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=1e9+10;
ll n,x,y,c; 
int main() {
	cin>>n>>x>>y>>c;
	x--;y--;
	ll l=0,r=2*n+1,ans=INF;
	while(l<=r) {
		ll mid=(l+r)/2;
		ll s=2*mid*mid+2*mid+1; //忽略邊界的點數
		if(mid-x>0) s-=(mid-x)*(mid-x); //上邊突出部分
		if(mid-(n-1-x)>0) s-=(mid-(n-1-x))*(mid-(n-1-x)); //下邊突出部分
		if(mid-y>0) s-=(mid-y)*(mid-y); //左邊突出部分
		if(mid-(n-1-y)>0) s-=(mid-(n-1-y))*(mid-(n-1-y)); //右邊突出部分
		
		if(mid-x-(n-y-1)-1>0) s+=(1+mid-x-(n-y-1)-1)*(mid-x-(n-y-1)-1)/2; //右上重疊部分
		if(mid-(n-1-x)-(n-1-y)>0) s+=(1+mid-(n-1-x)-(n-1-y)-1)*(mid-(n-1-x)-(n-1-y)-1)/2; //右下重疊部分
		if(mid-(n-1-x)-y-1>0) s+=(1+mid-(n-1-x)-y-1)*(mid-(n-1-x)-y-1)/2; //左下重疊部分
		if(mid-y-x-1>0) s+=(1+mid-y-x-1)*(mid-y-x-1)/2; //左上重疊部分

		if(s>=c) {
			r=mid-1;
			ans=min(mid,ans);
		}
		else {
			l=mid+1;
		}
	}
	cout<<ans<<endl;
	return 0;
}