1. 程式人生 > 其它 >Codeforces Beta Round #1 題解

Codeforces Beta Round #1 題解

前言

作為一名 OIer,刷題是必不可少的一項訓練,而 Codeforces 就是一個很好的刷題網站,每週都會舉辦一兩場比賽,難度也比較適中。最近想著從 Codeforces 舉辦的第一場比賽開始 VP(也不能說是 VP 吧,就是做一下里面的題),來提高一下自己的思維。

這篇題解是我在做完 Codeforces Beta Round #1 後一段時間才寫的,這場只有 3 道題(畢竟是 CF 的第一場比賽),題目難度中等,有一道簽到,一道模擬,以及一道計算幾何(以前從來沒有接觸過,看了題解才會的),做完之後呢就想著寫篇題解鞏固一下,結果拖到了現在才寫(囧)。


A. Theatre Square

題目:

\(a \times a\) 的石板覆蓋 \(n \times m\) 的長方形廣場,允許石板覆蓋的區域超出廣場,不允許打破石板,石板的兩側應平行於廣場兩側,要求覆蓋完廣場所需的石板數量。

樣例輸入 #1

6 6 4

樣例輸出 #1

4

題解:

小學生都會的題目。

分別計算出覆蓋長和覆蓋寬所需的石板數量,乘起來即可。

cin>>n>>m>>a;
	
ans=ceil(n*1.0/a)*ceil(m*1.0/a);
	
cout<<ans;

B. Spreadsheets

題目:

人們常用的電子表格軟體(比如: Excel)採用如下所述的座標系統:

第一列被標為 A,第二列為 B,以此類推,第 \(26\) 列為 Z。接下來為由兩個字母構成的列號: 第 \(27\) 列為 AA,第 \(28\) 列為 AB \(\cdots\) 在標為 ZZ 的列之後則由三個字母構成列號,如此類推。

行號為從 \(1\) 開始的整數。

單元格的座標由列號和行號連線而成。比如,BC23 表示位於第 \(55\)\(23\) 行的單元格。

有時也會採用被稱為 RXCY 的座標系統,其中 \(X\)\(Y\) 為整數,座標 \((X\),\(Y)\) 直接描述了對應單元格的位置。比如,R23C55 即為前面所述的單元格。

您的任務是編寫一個程式,將所給的單元格座標轉換為另一種座標系統下面的形式。

輸入

第一行一個整數 \(n\) \((1\) \(\le\) \(n\) \(\le\) \(10^5)\) 表示將會輸入的座標的數量。

接下來 \(n\) 行,每行一個座標。

注意: 每個座標都是正確的。此外不會出現行號或列號大於 \(10^6\) 的單元格。

輸出 \(n\) 行,每行一個被轉換的座標。

輸出

\(n\) 行,每行一個被轉換的座標。

樣例輸入 #1

2
R23C55
BC23

樣例輸出 #1

BC23
R23C55

題解:

首先我們需要判斷給定的座標是哪種表示方式。觀察到 RXCY 表示法中字元 C 前面一定有數字,於是可以進行如下判斷:

int pos=s.find('C');

if(pos!=string::npos && pos!=0 && isdigit(s[pos-1])){
	Handle RXCY...
}
else{
	Handle Excel...
}

我們先來處理 RXCY 格式的轉換,首先我們需要將行和列的數字從字串中分離出來:

int row=0,col=0;

for(int i=1;i<pos;i++) row=row*10+(s[i]-'0');

for(int i=pos+1;i<s.length();i++) col=col*10+(s[i]-'0');

再進行處理,不難發現行是不用變化的,只需變化列的數字即可,這裡相當於是將一個 10 進位制數字轉化成 26 進位制,不過該 \(26\) 進位制從 \(1\) 開始,\(1\) 對應 A,\(2\) 對應 B ... \(27\) 對應 AA。

我們可以將數字表示成 \(c_0*26^0+c_1*26^1+...+c_k*26^k\),其中 \(1 \leq c_i \leq 26\)。這樣每個 \(c_i\) 就對應了每一位的字母,如:\(28=2*26^0+1*26^1\),所以 \(28\) 就可以表示為 AB,於是我們可以寫出如下程式碼:

string toAlpha(int x){
    string ret="";

    while(x){
        ret=(char)((x-1)%26+'A')+ret;
        x=(x-1)/26;
    }
    
    return ret;
}

類似的,我們可以寫出 \(26\) 進位制轉為 \(10\) 進位制的程式碼:

int toNumber(string t){
    int ret=0;

    for(int i=t.length()-1;i>=0;i--)
        ret+=(t[i]-'A'+1)*pow(26,t.length()-i-1);

    return ret;
}

接下來就是處理 Excel 格式中的分離了,我們將用字母表示的列與用數字表示的行儲存在兩個變數中:

int row=0;
string col="";
bool flag=false;

for(int i=0;i<s.length();i++){
	if(!flag && isdigit(s[i])) flag=true;
	
	if(flag) row=row*10+(s[i]-'0');
	else col+=s[i];
}

最後,完整的程式碼便不難編寫了。


C. Ancient Berland Circus

題目:

給定三個點的座標,求出以這三個點為頂點的正多邊形的最小面積。

題解:

要求正多邊形面積最小,正多邊形的頂點都在給定三點構成的三角形外接圓上,如圖:

由三角形面積公式 \(S=ab \sin c\) 及正弦定理 \(\frac{a}{\sin A}=\frac{b}{\sin B}=\frac{c}{\sin C}=2R\) 可得三角形的外接圓半徑 \(R=\frac{abc}{4S}\),而 \(S\) 我們可以用海倫公式方便的算出來,於是此題轉為求正多邊形的最小邊數(半徑相同的正多邊形,邊數越少,面積越小)。

那如何求正多邊形的最小邊數呢?

觀察到給定三點構成三角形的三條邊所對的圓弧都是該正多邊形邊長所對圓弧的整數倍,於是我們只需求其圓心角的最大公約數即可,如圖,只需求 \(ang=\gcd(\alpha,\beta,\gamma)\),即可求出最小邊數 \(n=\frac{\ \ 360°}{ang}\)

Point A,B,C;

cin>>A>>B>>C;

a=dis(B,C);
b=dis(A,C);
c=dis(A,B);

p=(a+b+c)/2;
S=sqrt(p*(p-a)*(p-b)*(p-c));

R=a*b*c/4/S;

alpha=acos(1-a*a/(2*R*R));
beta=acos(1-b*b/(2*R*R));
gamma=acos(1-c*c/(2*R*R));

ang=gcd(gcd(alpha,beta),gamma);

n=2*PI/ang;

由於一些玄學的精度原因,求 \(ang\) 的程式碼要改為以下方法才可以通過 CF 的測試資料:

ang=gcd(gcd(alpha,beta),2*PI-alpha-beta);

接著就是求面積了,只需用一個小三角形的面積乘以邊數即可。

Area=R*R*sin(ang)*n/2;

此題求解完畢,不過浮點數計算誤差較大,還需多加註意。