兩個 long long 相加範圍會超過 long long,直接相加輸出會 overflow。所以有些人的直覺是此題可能需要實作大數加法才能通過。
不過啊,GNU 編譯器有提供一個對競程來說非常作弊的資料型態 __in128 可以用,但是沒辦法直接用 cin、cout 輸入輸出,所以必須自己實作 __128 的輸出。
#include<iostream>
#include<string>
using namespace std;
int main() {
long long x, y;
cin >> x >> y;
__int128 ans = (__int128)x + y;
if(ans == 0) {
cout << "0\n";
} else {
if(ans < 0) {
cout << "-";
ans = -ans;
}
string s;
while(ans) {
s = to_string((int)(ans % 10)) + s;
ans /= 10;
}
cout << s << '\n';
}
}
這題乍看之下很簡單,會讓人以為只要輸出 C*9/5+32
就行了但實際上這是錯的,這是因為 C++ 的除法並不是向下取整,而是向零取整!也就是說,當運算結果是正的時候,是向下取整,但是在運算結果是負的時候,是向上取整。但這題無論是在正的時候或是負的時候,運算都是要使用向下取整。
這裡提供兩種可能的解決方法有兩種:
由於輸入的數字範圍不大,所以只要從答案可能的最小值開始向上枚舉,找到最大的滿足
這樣做的好處就是當我們要判斷一個
此做法可使用二分搜優化時間複雜度。
由於癥結點在 C 是負數的時候整數除法非我們預期結果,那就想辦法讓除法運算的結果是非負就好啦,所以,我們先把 C 加上 100,再去乘以 (C+100)*9/5+32-180
即可在 C++ 算出正確答案。
#include<iostream>
using namespace std;
void solve() {
int C;
cin >> C;
for(int ans = -1000;; ans++) {
if(ans * 5 > C * 9 + 160) {
cout << ans - 1 << '\n';
return;
}
}
}
int main() {
int T;
cin >> T;
while(T--) {
solve();
}
}
#include<iostream>
using namespace std;
void solve() {
int C;
cin >> C;
cout << (C + 100) * 9 / 5 + 32 - 180 << '\n';
}
int main() {
int T;
cin >> T;
while(T--) {
solve();
}
}
此題應該是有各式各樣的寫法,這裡提供一個老師覺得最好寫,絕對不會錯的方法:
用 __int128 儲存數字,然後通分比較分子大小 : )
但相信很多人都是這樣寫但卻錯了,這是因為分母可能有負的,所以通分的結果可能和大家想的不一樣。
這裡提供一個可能解決方法:若
#include<iostream>
using namespace std;
void solve() {
long long a, b, c, d;
cin >> a >> b >> c >> d;
if(b * d < 0) {
a = -a;
b = -b;
}
if((__int128) a * d < (__int128) b * c) {
cout << "<\n";
} else if((__int128) a * d > (__int128) b * c) {
cout << ">\n";
} else {
cout << "=\n";
}
}
int main() {
int T;
cin >> T;
while(T--) {
solve();
}
}
此題是很經典的二分搜題,和 Codeforces EDU 裡的習題:B. Ropes 幾乎一模一樣,不會的人請好好去學習一下二分搜…網路上有很多教學影片。這題和 Codeforce 那題只差在此題輸入的數字的範圍比較大,要求的精度也比較高,如果使用 while(low + 1e-9 < hi)
這樣的二分搜寫法,且使用 low 和 hi 使用精度比 double 還低的資料型態的話,在此題會超時。所以建議的寫法有以下 2 種:
不只要判斷 low 和 hi 的絕對值是否小於 1e-9,還得再判斷 low 和 hi 的相對誤差是否小於 1e-9
直接固定二分搜縮小範圍的次數,例如縮小 100 次就相當足夠了。
用 long double 雖然可以通過這題,但無法保證哪天是否會不小心遇到連 long double 也處理不了經度的題目。
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long double LD;
const int SIZE = 500000;
int a[SIZE];
int main() {
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++) {
cin >> a[i];
}
LD low = 0, hi = *max_element(a, a + n);
for(int round = 0; round < 100; round++) {
long long num = 0;
LD mm = (low + hi) * 0.5;
for(int i = 0; i < n; i++) {
num += (long long)floor(a[i] / mm);
}
if(num >= m) { low = mm; }
else { hi = mm; }
}
cout << (low + hi) * 0.5 << '\n';
}
偶數的情況比較容易猜到,不妨假設
也就是說,當
之所以會想到要這樣構造,是因為我們會想要讓在重新排列後的相鄰兩個數字,在原來排序好的數列裡距離最近的那兩個盡可能遠,而這樣構造就能使得此重新排列裡相鄰兩個數字在排序好的數列裡編號差至少都達到
至於證明可使用鴿籠原理:考慮
那麼
也就是說,我們證明了無論怎麼排列,相鄰的兩個數的差的絕對值的最小值一定小於等於
而上面那個構造方法剛剛好就是上述等號成立的情況。
已
有沒有發現什麼呢?
另
第一個種情形公式化來說的話會是:
而之後沒種情形都是把上一種情形的前
用圖像化來思考的話,我們可以把第一個數列圍成一個圈圈,你就會發現所有相鄰兩個數恰好是所有
偶數情況是比較容易想到及證明的,於是我們可能會這樣想:奇數的作法會不會也和偶數的情形類似呢?
在複習一次偶數的情況:對於每個
但稍微構造下就會發現,有辦法構造出更好的答案!
這是因為偶數的情況的證明形式是:
對於所有合法的
但在奇數的情況,其實允許至多一個
所以答案一定不會超過