1. 程式人生 > 實用技巧 >題解 【2020普專提十連測Day7刷題賽】蜂國建設者

題解 【2020普專提十連測Day7刷題賽】蜂國建設者

【2020普專提十連測Day7刷題賽】蜂國建設者

10pts

只有 \(9\) 種情況,手玩打表即可

20pts

設所有子六邊形個數為 \(ans1\) ,面積之和為 \(ans2\) ,顯然 \(ans=\frac{ans2}{ans1}\)

暴力列舉

Task 5 30pts

為方便敘述,本題解中使用 包含的單元格\(-1\) 表示邊的長度,即為題目中所述邊長 \(-1\)

這種性質下只有形如 \((1,1,x)\) 的子六邊形,其面積為 \(3x+4\)

把每個子六邊形簡化成“中心線”上的一條線段

於是轉化到了一維的情況,這個與這道題的簡化版 討論 方法是一樣的

\(ans1\) :列舉端點,$ ans1=\sum\limits_{i=1}^{c}(c-i+1) = c^2-\frac{c(c+1)}{2}+c = \frac{c(c+1)}{2} $

\(ans2\) :列舉長度,$ ans2=\sum\limits_{i=1}^{c}(c-i+1)(3i+4)$

\(O(c)\) 已經可以輕鬆通過了

\(ans2\) 的式子可以進一步化開 \(O(1)\) 計算,具體可以參照 討論 ,這裡不過多贅述。最後得到的是 \(ans=c+5\)

結合以上方法可得到 \(40-50pts\)

#include<bits/stdc++.h>
using namespace std;
long long a,b,c;
int main(){
	scanf("%lld%lld%lld",&a,&b,&c);
	printf("%.3lf",1.0*(c+5));
}

6行程式碼就有 30pts​ ,是不是很良心

60pts

先固定三邊 \(a\), \(b\), \(c\) ,考慮列舉每種子六邊形對應的三邊 \(i\), \(j\), \(k\),計算個數

記邊 \(x\) 的對邊為 \(x'\) ,比如 \(a\) 的對邊就是 \(a'\)

有一種想法是 \(i \leq a\) , \(j \leq b\), \(k \leq c\) ,這顯然是錯誤的,以下是反例

顯然正確的是 \(i+j \leq a+b\) , \(i+k \leq a+c\), \(j+k \leq b+c\) (可以補成等邊三角形),因此 \(i\), \(j\), \(k\)

的範圍不會太大,保證列舉的時間複雜度為 \(O(abc)\)

考慮如何計算一種大小的子六邊形的個數 \(cal(i,j,k)\)

首先將它 \(j\)\(k\) 交點放在 \(b\)\(c\) 交點,顯然當 \(a \leq b \leq c\) 時是做得到的

考慮一步一步向下移,每一步計算向左下和右下分別能走 \(l\)\(r\)

模擬一下發現,前半段 \(l\)\(r\) 是不變的,而後半段它們逐漸減小,直到有一個變為 \(0\)

\(l\)\(r\) 是等價的,先只考慮 \(l\) ,假設移動了 \(t\)

\(i\) 向左下移動到直線 \(a\) 的距離為 \(b-j\)

\(k'\) 向下移動到直線 \(c'\) 的距離為 \(a+b-i-j-t\)

顯然 \(l=min(b-j,a+b-i-j-t)\)

同理 \(r=min(c-k,a+c-i-k-t)\)

再來考慮 \(t\) 的最大值,即是 **\(k'\) 向下移動到直線 \(c'\) 的距離 \(a+b-i-j\) **和 **\(j'\) 向下移動到直線 \(b'\) 的距離 \(a+c-i-k\) **的最小值

注意到自身的狀態也要算,故

\(cal(i,j,k)=\sum\limits_{t=0}^{min(a+b-i-j,a+c-i-k)}(min(b-j,a+b-i-j-t)+min(c-k,a+c-i-k-t)+1)\)

需要注意負數, \(b-j \geq 0\) , $ c-k \geq 0$ ,可以直接在列舉時剪枝優化(優化 \(1\)

前面提到 \(i+j \leq a+b\) , \(i+k \leq a+c\), \(j+k \leq b+c\)

固定 \(i\) 的範圍,就可以列舉 \(j \leq a+b-i\)\(k \leq a+c-i\) (優化 \(2\)

至於計算 \(ans2\) ,分割可知每個子六邊形的面積為 \(S(i,j,k)=i(j+1)+j(k+1)+k(i+1)+1\)

時間複雜度 \(O((abc)^2)\)

常數小,可以通過 \(Task \ 1,2,3,5 +Task \ 4_1\) \(75pts\)

還要注意只有當 \(a \leq b \leq c\) 時才能這樣做,否則會出現這種情況

顯然 \(ans(a,b,c)=ans(a,c,b)\) ,在開始時需要將 \(a,b,c\) 從小到大排序,否則只有 \(50 pts\)

#include<bits/stdc++.h>
using namespace std;
long long a,b,c;
long long ans1,ans2;
long long cal(long long i,long long j,long long k){
	long long ans=0;
	long long x=a+b-i-j;
	long long y=a+c-i-k;
	while(x>=0 && y>=0){
		ans+=min(x,b-j)+min(y,c-k)+1;
		x--;y--;
	}
	return ans;
}
int main(){
	scanf("%lld%lld%lld",&a,&b,&c);
	if(a>b) swap(a,b);if(b>c) swap(b,c);if(a>b) swap(a,b);
	a--,b--,c--;
	for(long long i=1;i<=a+min(b,c);i++)
		for(long long j=1;j<=min(b,a+b-i);j++)
			for(long long k=1;k<=min(c,a+c-i);k++){
				long long t=cal(i,j,k);
				ans1+=t;
				ans2+=t*(i*(j+1)+j*(k+1)+k*(i+1)+1);
			}
	printf("%.3lf",1.0*ans2/ans1);
}

85-100pts

考慮優化 \(cal(i,j,k)\)

前提還是有 \(b-j \geq 0\) , $ c-k \geq 0$ ; \(i+j \leq a+b\) , \(i+k \leq a+c\), \(j+k \leq b+c\)

\(lim=min(a+b-i-j,a+c-i-k)\)

\(cal(i,j,k)\)

\(=\sum\limits_{t=0}^{lim}(min(b-j,a+b-i-j-t)+min(c-k,a+c-i-k-t)+1)\)

\(=\sum\limits_{t=0}^{lim}(min(0,a-i-t)+b-j+min(0,a-i-t)+c-k+1)\)

\(=2\sum\limits_{t=0}^{lim}min(0,a-i-t)+(b-j+c-k+1)(lim+1)\)

現在只考慮前面那部分,當 \(t \leq a-i\) 時為 \(0\) ,否則為 \(a-i-t\) ,再令 \(d=max(0,a-i+1)\) ,所以

\(cal(i,j,k)=2\sum\limits_{t=d}^{lim}(a-i-t)+(b-j+c-k+1)(lim+1)\)

\(d>lim\) ,則 \(cal(i,j,k)=(b-j+c-k+1)(lim+1)\)

否則,

\(cal(i,j,k)\)

\(=-2\sum\limits_{t=d}^{lim}t+2(a-i)(lim-d+1)+(b-j+c-k+1)(lim+1)\)

\(=-(d+lim)(lim-d+1)+2(a-i)(lim-d+1)+(b-j+c-k+1)(lim+1)\)

\(=(2a-2i-d-lim)(lim-d+1)+(b-j+c-k+1)(lim+1)\)

這個式子已經可以 \(O(1)\) 計算出 \(cal(i,j,k)\)

還可以把 \(cal\) 直接搬到主函式來

時間複雜度 \(O(abc)\)

已經可以通過本題

不優化只能得到 \(85pts\)

#include<bits/stdc++.h>
using namespace std;
long long a,b,c;
long long ans1,ans2;
int main(){
	scanf("%lld%lld%lld",&a,&b,&c);
	a--,b--,c--;
	if(a>b) swap(a,b);if(b>c) swap(b,c);if(a>b) swap(a,b);
	for(long long i=1;i<=a+min(b,c);i++){
		for(long long j=1;j<=min(b,a+b-i);j++){
			for(long long k=1;k<=min(c,a+c-i);k++){
				long long t=0;
				long long lim=min(a+b-i-j,a+c-i-k);
				long long d=max(0LL,a-i+1);
				if(d<=lim) t=(lim-d+1)*(2*a-2*i-d-lim);
				t+=(b-j+c-k+1)*(min(a+b-i-j,a+c-i-k)+1);
				ans1+=t;
				ans2+=t*(i*(j+1)+j*(k+1)+k*(i+1)+1);
			}
		}
	}
	printf("%.3lf",1.0*ans2/ans1);
}