Codeforces Round #670 (Div. 2)
A. Subset Mex
貪心。
B. Maximum Product
負數的情況只可能為0個,2個或者4個,列舉一下這所有的情況然後取個最大值就行。
C. Link Cut Centroids
首先容易發現一顆樹至多兩個重心。
一個重心的情況好處理,任意新增一條邊即可。
兩個重心的情況對應著一條邊為橋,並且左右兩部分的點數相同。這種情況只需要任選一個葉子結點,然後將其新增入這條邊的另外一個端點即可。易證不會再一次出現兩個重心的情況。
Code
// Author : heyuhhh // Created Time : 2020/09/12 22:07:43 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n; cin >> n; vector<vector<int>> G(n); for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; --u, --v; G[u].push_back(v); G[v].push_back(u); } vector<int> size(n), up(n); int Max = INF, x = -1, y = -1; function<void(int, int)> dfs = [&] (int u, int fa) { up[u] = fa; size[u] = 1; for (auto& v : G[u]) if (v != fa) { dfs(v, u); size[u] += size[v]; } int tot = 0; int maxv = 0; for (auto& v : G[u]) if (v != fa) { tot += size[v]; maxv = max(maxv, size[v]); } maxv = max(maxv, n - 1 - tot); if (Max > maxv) { Max = maxv; x = u, y = -1; } else if (Max == maxv) { y = u; } }; dfs(0, -1); if (n & 1 || y == -1) { cout << 2 << ' ' << up[1] + 1 << '\n'; cout << 2 << ' ' << up[1] + 1 << '\n'; return; } int lf; for (auto v : G[x]) { if (v != y) { lf = v; break; } } cout << lf + 1 << ' ' << up[lf] + 1 << '\n'; cout << lf + 1 << ' ' << y + 1 << '\n'; } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); int T; cin >> T; while(T--) run(); return 0; }
D. Three Sequences
問題轉化過後直接維護 \(a\) 的差值就行,只有非負的差值會產生貢獻。
Code
// Author : heyuhhh // Created Time : 2020/09/13 09:42:29 #include<bits/stdc++.h> #define MP make_pair #define fi first #define se second #define pb push_back #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; //head const int N = 1e5 + 5; void run() { int n; cin >> n; vector<ll> a(n); for (int i = 0; i < n; i++) cin >> a[i]; vector<ll> d(n); ll sum = 0; for (int i = 1; i < n; i++) { d[i] = a[i] - a[i - 1]; if (d[i] >= 0) sum += d[i]; } auto get = [&] () { ll res = sum + a[0]; if (res > 0) return (res + 1) / 2; return res / 2; }; auto add = [&] (int p, int v) { if (d[p] > 0) sum -= d[p]; d[p] += v; if (d[p] > 0) sum += d[p]; }; cout << get() << '\n'; int q; cin >> q; while (q--) { int l, r, x; cin >> l >> r >> x; --l, --r; if (l) add(l, x); else a[0] += x; if (r + 1 < n) add(r + 1, -x); cout << get() << '\n'; } } int main() { #ifdef Local freopen("input.in", "r", stdin); #endif ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); run(); return 0; }
E. Deleting Numbers
題意:
互動題。
一開始給定一個數 \(n\),會產生一個 \(\{1,2,\cdots,n\}\)這樣的集合。然後要猜測一個數 \(x,x\leq n\)。
之後可以執行不超過 \(10000\) 次操作,操作有三種:
- \(A\ a\):表示詢問集合中有多少個數為 \(a\) 的倍數;
- \(B\ b\):表示詢問集合中有多少個數為 \(a\) 的倍數,並且在集合中把這些數刪去。
- \(C\ x\),用於回答 \(x\) 是多少。
最後就是通過第三種操作回答 \(x\)。
思路:
因為 \(n\leq 10^5\),所以 \(x\) 至多有一個 \(\geq 330\)
那麼對於較小的那些質因子,顯然可以直接暴力詢問來計算,比如先詢問 \(B\ p\),再詢問一次 \(A\ p\)看個數是否為 \(0\),如果不為 \(0\) 說明 \(p\) 為其中一個質因子,然後再列舉 \(p\) 的次冪來詢問。
對於較大的質因子,樸素的想法就是對每個質因子類似於剛剛那樣詢問兩次,但顯然總的操作次數不允許。注意到這其實是一個數量變化的關係,並且每次至多變化 \(1\),那麼其實可以對質因子進行分塊,也就是說用 \(A\) 操作來統計總的個數,然後在 \(B\) 操作了一定數量的質因子過後,再通過 \(A\) 操作來統計次數。這樣就能快速的知道質因子存在於哪一塊,把總的詢問次數降下來了。
注意還有一個細節,就是處理完較小的質因子過後,如果當前答案為 \(1\),那麼直接上面這樣做是沒問題的。但是如果答案不為 \(1\),直接上面那樣做可能會有問題。因為比如說 \(n=x=6\),那麼我處理較大質因子時數量的變化是正確的,因為每次我只會刪掉當前這個質數,但是不能找到答案。所以這種情況需要特殊處理。
詳見程式碼:
Code
// Author : heyuhhh
// Created Time : 2020/09/13 10:20:40
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
int primes[N], tot;
bool vis[N];
void init(int n) {
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
primes[++tot] = i;
}
for (int j = 1; j <= tot && primes[j] * i <= n; j++) {
vis[primes[j] * i] = true;
if (i % primes[j] == 0) {
break;
}
}
}
}
void run() {
int n;
cin >> n;
init(n);
int t = 1;
while (t * t <= n) ++t;
auto query = [&] (ll x, int op = 1) {
if (x > n) return 0;
if (op == 1)
cout << "B " << x << endl;
else
cout << "A " << x << endl;
cout << endl;
int y; cin >> y;
return y;
};
int p = 1;
int ans = 1;
for (; p <= tot && primes[p] < t; p++) {
query(primes[p]);
int x = query(primes[p]);
if (x) {
int now = primes[p] * primes[p];
for (int i = 2; now / primes[p] <= n; now *= primes[p]) {
if (!query(now)) {
ans *= now / primes[p];
break;
}
}
}
}
if (ans != 1) {
for (; p <= tot; p++) {
int x = query((ll)ans * primes[p]);
if (x > 0) {
ans *= primes[p];
break;
}
}
} else {
int B = 95;
int last = query(1, 2);
for (int cnt = 0; p <= tot; ++p) {
query(primes[p]);
if (++cnt == B || p == tot) {
int now = query(1, 2);
if (last - now != cnt) {
for (int i = p - cnt + 1; i <= p; i++) {
int x = query(primes[i]);
if (x) {
cout << "C " << ans * primes[i] << endl;
return;
}
}
}
last = now;
cnt = 0;
}
}
}
cout << "C " << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}