Codeforces Round #725 (Div. 3) A~G 題解記錄
補題連結:Here
1538A. Stone Game
陣列 \(a\) 的大小為 \(n\) ,請問每次可以刪除最左和最右側的元素,請問最少執行多少次能刪除掉陣列中的最大值和最小值 (\(1\le a_i\le n\))
在輸入的時候確定最大值和最小值的下標,
4種情況
比較從左邊刪除和右邊刪除的情況即可
void solve() { int n; cin >> n; int id1, id2; for (int i = 1, x; i <= n; ++i) { cin >> x; if (x == 1)id1 = i; if (x == n)id2 = i; } if (id1 > id2) swap(id1, id2); int cnt1 = (id1 + min(n - id2 + 1, id2 - id1)); int cnt2 = (n - id2 + 1 + min(id1, id2 - id1)); cout << min(cnt1, cnt2) << "\n"; }
void solve() { int n; cin >> n; vector<int>a(n); for (int &x : a)cin >> x; int maxPos = max_element(a.begin(), a.end()) - a.begin(); int minPos = min_element(a.begin(), a.end()) - a.begin(); cout << min({ max(maxPos, minPos) + 1, (n - 1) - min(maxPos, minPos) + 1, (n - 1) - maxPos + minPos + 2, (n - 1) - minPos + maxPos + 2 }) << "\n"; }
1538B. Friends and Candies
Polycarp 要對他的 \(n\) 個朋友的蛋糕數量重新分配,使得每個人的蛋糕數量相同。
找到最小的 \(k\) \(a_{i1} + a_{i2}+...+a_{ik}\) 進行分配
數學題,
首先如果總的蛋糕數不是 \(n\) 的倍數,直接輸出 \(-1\) ,接下來在統計大於平均值的人數即可
void solve() { int n; cin >> n; ll cnt = 0; vector<int>a(n); for (int &x : a)cin >> x, cnt += x; if (cnt % n != 0) {cout << -1 << "\n"; return ;} int c = 0; cnt /= n; for (int i = 0; i < n; ++i) { if (a[i] > cnt)c++; } cout << c << "\n"; }
1538C. Number of Pairs
在陣列 \(a\) 中尋找最大對數的 \((i,j) (1\le i < j \le n)\) 使得 \(l \le a_i + a_j \le r\)
簡單來說要統計兩種情況
- \(a_i + a_j \le r\)
- \(a_i + a_j \le l - 1\)
二分上面兩種情況,然後相減就是合適的區間長度了。
但要注意如果本身 \(a_i * 2\) 就符合情況的話會重複計算一次,要減去一
最後輸出 \(ans / 2\)
雙指標寫法
void solve() {
int n; ll L, R;
cin >> n >> L >> R;
vector<ll>a(n + 1);
for (int i = 1; i <= n; ++i)cin >> a[i];
sort(a.begin() + 1, a.end());
int l = 2, r = n;
ll cnt = 0;
while (l <= n and a[1] + a[1] <= L)l++;
for (int i = 1; i <= n; ++i) {
while (l > 1 and a[l - 1] + a[i] >= L)l--;
while (r > i and a[r] + a[i] > R)r--;
if (r >= max(l, i)) {
if (l > i)cnt += (r - l + 1);
else cnt += r - i;
}
}
cout << cnt << '\n';
}
二分寫法
void solve() {
int n, l, r ;
cin >> n >> l >> r;
vector<int>a(n);
for (int &x : a)cin >> x;
sort(a.begin(), a.end());
ll ans = 0;
for (int i = 0; i < n; ++i) {
ans += upper_bound(a.begin(), a.end(), r - a[i]) - a.begin();
ans -= lower_bound(a.begin(), a.end(), l - a[i]) - a.begin();
if (2 * a[i] >= l && a[i] * 2 <= r)ans--;
}
cout << ans / 2 << "\n";
}
1538D. Another Problem About Dividing Numbers
給與 \(a\) 和 \(b\) 兩個整數,在一回合操作裡,
- \(a\) 和 \(b\) 兩個整數都要找出 \(c\ (a\ 或\ b\ \% c ==0)\)
請問是否能在 \(k\) 個回合結束後使得 \(a=b\)
看樣例想到是質因數個數的問題,
但試了下先尤拉素數篩晒出 \(1e9\) 的資料還是 TLE了(常數太大了),然後嘗試直接統計因子個數,注意使用 scanf
而不是 cin
)
void solve() {
int a, b, k;
scanf("%d%d%d", &a, &b, &k);
if (k == 1) {
puts(a != b && (a % b == 0 || b % a == 0) ? "YES" : "NO");
return ;
}
int cnt = 0;
for (int i = 2; i * i <= b; ++i) {
while (b % i == 0) {
b /= i; cnt++;
}
}
if (b != 1)cnt++;
for (int i = 2; i * i <= a; ++i) {
while (a % i == 0) {
a /= i;
cnt++;
}
}
if (a != 1)cnt++;
puts(cnt >= k ? "YES" : "NO");
}
1538E. Funny Substrings
沒看懂題意,以後補
1538F. Interesting Function
這道題比賽沒寫出來虧了一個億
給定兩個整數 \(l\) 和 \(r\),其中 \(l<r\)。 我們將向 \(l\) 加 1,直到結果等於 \(r\)。 因此,將執行 \(r-l\) 次加法。 對於每個這樣的加法,讓我們看看在它之後將更改的位數。
累加不同位數的情況下 \(r - l\) ,直到 \(l = 0\) 和 \(r= 0\)
void solve() {
ll l, r;
cin >> l >> r;
ll ans = 0;
while (l != 0 || r != 0) {
ans += (r - l);
l /= 10, r /= 10;
}
cout << ans << "\n";
}
如果這算數位DP, 那麼算是我見過最簡單的數位DP了.
ll dp[20];
void init(int n) {
dp[1] = 1;
for (int i = 2; i <= n; ++i)dp[i] = dp[i - 1] * 10 + 1;
}
int cnt(ll x) {
int a[20] = {0}, Cnt = 0;
while (x) {
a[Cnt++] = x % 10;
x /= 10;
}
ll ans = 0;
for (int i = Cnt - 1; i >= 0; --i)ans += dp[i + 1] * a[i];
return ans;
}
void solve() {
ll l, r;
cin >> l >> r;
cout << cnt(r) - cnt(l) << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
init(15);
int _; for (cin >> _; _--;) solve();
return 0;
}
1538G. Gift Set
Polycarp 有 x 個紅色糖果和 y 個藍色糖果。 使用它們,他想製作禮品套裝。 每個禮品套裝包含一個紅色糖果和 b 個藍色糖果,或一個藍色糖果和 b 個紅色糖果。 任何糖果最多隻能屬於一個禮品套裝。
幫助 Polycarp 找到他可以創造的最大數量的禮品套裝。
例如,如果 \(x=10,y=12,a=5,b=2\),那麼 Polycarp 可以製作三套禮物:
- 第一套有 5 個紅色糖果和 2 個藍色糖果;
- 第二組將有 5 個藍色糖果和 2 個紅色糖果;
- 第三組將有 5 個藍色糖果和 2 個紅色糖果。
思路來自 Arctic_Clam
考慮二分可分的集合的數量。
如何 \(O(1)\) 求給定的某個集合可行與否?
不妨設 \(x < y,a < b\)
記 \(n\) 為要驗證的集合數量,\(s\) 為第 一種集合數量,\(t\)為 第二種集合數量。
於是我們有
變形可以得到
\[\left\{ \begin{array}{**lr**} s \ge \frac{x - nb}{a -b}\\ s \le \frac{y - na}{b - a} \end{array} \right. \]也就是說,給定n,我們可以求出s的取值區間。再判定下該區間是否合法即可。
\(eg:\) \(s\) 取值區間的左界應該是向下取整,但是 \(x−nb\) 和 \(a − b\) 都是負數,而 C++
預設是趨零取整,所以要特判。
ll x, y, a, b;
bool check(ll n) {
ll l = (x - n * b) / (a - b);
if ((x - n * b) < 0 and (-(x - n * b)) % (b - a) != 0)l++;
ll r = (y - n * a) / (b - a);
return l <= r and r >= 0 and l <= n;
}
void solve() {
cin >> x >> y >> a >> b;
if (x > y) swap(x, y);
if (a > b) swap(a, b);
if (a == b) {
cout << (x /= a) << "\n";
return ;
}
ll l = 0, r = x;
while (l < r) {
if (l == r - 1) {
if (check(r)) l = r;
break;
}
ll mid = (l + r) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << l << '\n';
}
似乎本題也可以三分做,dalao也對三分做了正確性證明(tql
二分的正確性是顯然的,但是三分的正確性並不那麼顯然。
為了證明三分的正確性,我們需要證明集合總數n是第一集合數s的函式,且該函式至多有一個峰。
下面給出證明: