題解 【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\)
考慮如何計算一種大小的子六邊形的個數 \(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);
}