Codeforces Round 1129 (Div. 2) 题解
ToBeDetermined · · 题解
Link
A - Subset Selecting
直接从小到大贪心,维护当前没有使用的元素数量即可。
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 105;
int n, k, a, c[N], s;
bool t;
void solve() {
cin >> n >> k, s = t = 0;
for(int i = 0; i <= n; ++i) {
c[i] = 0;
}
for(int i = 1; i <= n; ++i) {
cin >> a, ++c[a];
}
for(int i = 0; i <= n; ++i) {
if(not c[i] or s + c[i] < k) {
cout << i << '\n';
return;
}
s += c[i] - k;
}
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::
B - Sequence Decreasing
先考虑长度为
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
int n, a, mn;
void solve() {
cin >> n, mn = n;
for(int i = 1; i <= n; ++i) cin >> a, mn = min(mn, a);
if(mn < n) cout << n << '\n';
else cout << "0\n";
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::
C - Edge Addeleting
感觉是很好的 D2C。
注意到如果
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 500'005;
int n, m, d[N], ans;
vector<int> to[N];
queue<int> q;
void solve() {
cin >> n >> m;
for(int u = 1; u <= n; ++u) {
d[u] = -1, to[u].clear();
}
for(int _ = 1; _ < n; ++_) {
int u, v; cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u);
}
d[n] = 0, q.push(n);
while(q.size()) {
int u = q.front();
q.pop();
if(u == 1) continue;
for(int v : to[u]) {
if(d[v] < 0) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
ans = d[1];
while(m--) {
int v; cin >> v;
ans = max(ans, d[v] + 1);
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::
D - Number Operating
这题个人差似乎挺大的,可能会有点创人;感觉 D2 有点难,本来想放后面,但调度员没同意。最后看来 D2 也成为了整场最大的骂点。
容易想到把若干个操作一视作一组,即花费
- 进行一次代价为
k 的操作与一次操作二,此时n' = n - 2^k + 2 ,有f(n) = f(n - 2^k +2) + k + 1 。 - 进行一次代价为
k + 1 的操作与若干次操作二,有f(n) = k + 1 + 2^{k + 1} - 1 - n = 2^{k + 1} - n + k 。
容易证明此时进行代价
每次递归
但还可以更优。设
注意到递归过程中我们只会访问
但还可以更优。注意到
但还可以更优。考虑
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
#define int long long
constexpr int mod = 998'244'353;
constexpr int inv2 = (mod + 1) >> 1;
int l, r, pos[60], pre[60];
int g(int n) {
if(n <= 1) return max(n, 0ll);
if(n == 2) return 4;
if(n == 3) return 6;
int k = __lg(n), t = min(n - (1ll << k) + 2, pos[k]), res = 0;
res = pre[k];
res = (res + g(t) + mod - 1 + (t - 1) % mod * (k + 1)) % mod;
t += (1ll << k) - 2, res = (res + ((1ll << (k + 2)) + 2 * k - t - n - 1) % mod * ((n - t) % mod) % mod * inv2) % mod;
return res;
}
void solve() {
cin >> l >> r;
cout << (g(r) + mod - g(l - 1)) % mod << '\n';
return;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
for(int k = 0; k < 60; ++k) {
int rem = (1ll << k) + 1;
pos[k] = 1;
for(int i = k; i >= 2; --i)
if((1ll << i) + i - 1 <= rem)
rem -= (1ll << i) + i - 1, pos[k] += (1ll << i) - 2;
pre[k] = g((1ll << k) - 1);
}
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::
E - Tree Guessing
挺有意思的交互。这题原本放的 F,Token Re-Moving 原本放的 E,但是因为太多 tester 和这题对上脑电波就被放到 E 了。
交互库返回的东西的信息太杂了,考虑搞些有用的信息出来。若令根的权值为
不妨令
此时以当前点为根询问的结果等于以它的父亲为根询问的结果,于是只需要给它前面的点构造一个权值序列使得以每个点询问所得权值互不相同即可。容易想到按 BFS 序构造,并容易证明这是正确的。求出父亲后把它加到树上即可。
询问次数
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 505;
int n, sz[N], p[N], a[N], cur, res, tmp;
vector<int> to[N];
queue<int> q;
int query(int x, int *p) {
cout << "? " << x;
for(int u = 1; u <= n; ++u) {
cout << ' ' << p[u];
}
cout << endl;
int res; cin >> res;
return res;
}
void solve() {
cin >> n;
p[1] = n, p[2] = n - 1;
for(int u = 3; u <= n; ++u) {
p[u] = u - 2;
}
for(int u = 2; u <= n; ++u) {
swap(p[u - 1], p[u]);
sz[u] = query(1, p) - n * (n - 1);
a[u] = u;
}
a[1] = 1;
sort(a + 2, a + n + 1, [](int u, int v) {
return sz[u] > sz[v];
});
for(int i = 2; i <= n; ++i) {
cur = 1;
q.push(1);
while(q.size()) {
int u = q.front();
q.pop();
p[u] = ++cur;
for(int v : to[u]) {
q.push(v);
}
}
p[a[i]] = 1;
for(int j = i + 1; j <= n; ++j) {
p[a[j]] = j;
}
res = query(a[i], p);
res -= (i + 1 + n) * (n - i) / 2 + i;
for(int j = 1; j < i; ++j) {
tmp = p[a[j]] - 1;
if(res == tmp * (i - 1) + (i - tmp) * (i - tmp - 1) / 2) {
to[a[j]].push_back(a[i]);
}
}
}
cout << "!\n";
for(int u = 1; u <= n; ++u) {
for(int v : to[u]) {
cout << u << ' ' << v << '\n';
}
to[u].clear();
}
fflush(stdout);
return;
}
int main() {
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::
F - Token Re-Moving
感觉这题会被骂,最开始投的 D2E 不知道为啥就给过了,后来又被换到 D2F 了。验题的时候有 CM 秒了,有 M 不会,有 IM 写其他做法没卡过去,也有 IGM 评价这题为 "Amazing problem"。不太懂。
这场本来有个叫 Token Moving 的题的,题目名在致敬 CF2119D,后来有了这题就自然而然地取名 Token Re-Moving 了()。然而后来 Token Moving 被删了就只剩 Token Re-Moving 了。
直接做应该是没啥前途的,于是反过来考虑一个点
考虑维护这个过程。删点是困难的,考虑倒过来变成加点并维护重构树。于是剩下的就是简单的了。加入点
:::success[Code]
#include <bits/stdc++.h>
using namespace std;
constexpr int mod = 998'244'353;
constexpr int N = 500'005;
void M(int &x) { x -= (x >= mod ? mod : 0); }
int n, a[N], p[N], fa[N], ans[N], tmp;
vector<int> to[N], nto[N];
bool vis[N];
struct DSU {
int fa[N];
void init(int n) {
for(int x = 1; x <= n; ++x) {
fa[x] = x;
}
}
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if(x == y) return;
fa[x] = y;
}
} dsu;
void dfs1(int u) {
for(int v : to[u]) {
if(v != fa[u]) {
fa[v] = u, dfs1(v);
M(a[u] += a[v]);
}
}
}
void dfs2(int u) {
for(int v : nto[u]) {
M(ans[v] += ans[u]);
dfs2(v);
}
}
void solve() {
cin >> n;
for(int u = 1; u <= n; ++u) {
to[u].clear(), nto[u].clear(), ans[u] = 0;
}
for(int _ = 1; _ < n; ++_) {
int u, v; cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u);
}
for(int u = 1; u <= n; ++u) {
cin >> a[u];
}
for(int i = 1; i <= n; ++i) {
cin >> p[i];
}
dfs1(1);
dsu.init(n);
for(int u = 1; u <= n; ++u) {
vis[u] = 0;
}
for(int i = n; i; --i) {
int &u = p[i];
for(int v : to[u]) {
if(vis[v]) {
if(v == fa[u]) {
v = dsu.find(v);
M(ans[v] += a[u]);
} else {
M(tmp = a[1] + mod - a[v]);
v = dsu.find(v);
M(ans[v] += tmp);
}
nto[u].push_back(v);
dsu.merge(v, u);
}
}
vis[u] = 1;
}
M(ans[p[1]] += a[1]), dfs2(p[1]);
for(int u = 1; u <= n; ++u) {
cout << ans[u] << " \n"[u == n];
}
return;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while(T--) solve();
return 0;
}
:::