Codeforces Global Round 14
A - Phoenix and Gold
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> n >> m;
rep (i, 1, n) cin >> a[i];
sort(a + 1, a + 1 + n);
int x = 1, s = 0; queue<int> q;
for (; x <= n; ++x) {
s += a[x];
if (s == m && q.empty() && x == n) break;
else if (s == m && (q.empty() || q.front() == a[x])) {
int y = x + 1;
while (y <= n && a[y] == a[x]) ++y;
if (y > n) break;
q.push(a[y]);
while (x < y) q.push(a[x++]), s += a[x];
}
else if (s == m && x == n) break;
else if (s == m) {
q.push(a[x]); q.push(a[++x]); q.push(q.front());
q.pop(); s += a[x];
}
else q.push(a[x]);
}
if (s == m) cout << "NO
";
else {
cout << "YES
";
while (!q.empty()) cout << q.front() << ' ', q.pop();
cout << '
';
}
}
return 0;
}
B - Phoenix and Puzzle
int main() {
IOS; set<int> st; n = 1;
while (1) {
st.insert(n * n); ++n;
if (n >= 1e9 / n) break;
}
for (cin >> _; _; --_) {
cin >> n;
if (n & 1) cout << "NO
";
else if (st.count(n >> 1)) cout << "YES
";
else if (n >> 1 & 1) cout << "NO
";
else if (st.count(n >> 2)) cout << "YES
";
else cout << "NO
";
}
return 0;
}
C - Phoenix and Towers
贪心
int main() {
IOS;
for (cin >> _; _; --_) {
priority_queue<PII> q;
cin >> n >> m >> k; cout << "YES
";
rep (i, 1, m) q.push({ 0, i });
rep (i, 1, n) {
cin >> cas;
auto x = q.top(); q.pop();
cout << x.se << ' ';
x.fi -= cas; q.push(x);
}
cout << '
';
}
return 0;
}
D - Phoenix and Socks
int main() {
IOS;
for (cin >> _; _; --_) {
cin >> k >> n >> m; map<int, int> a, b;
int x = n, y = m, d = 0;
rep (i, 1, n) cin >> cas, ++a[cas];
rep (i, 1, m) {
cin >> cas;
if (a[cas]) --a[cas], --x, --y;
else ++b[cas];
}
if (x == y) { cout << x << '
'; continue; }
if (x < y) swap(a, b), swap(x, y);
for (auto &i : a) d += i.se >> 1;
if (d >= x - y >> 1) cout << y + (x - y >> 1) << '
';
else cout << y + (x - y - d * 2) + d << '
';
}
return 0;
}
E - Phoenix and Computers
线性dp
首先, 假设开([l, r]) 开了 (r-l+1) 台电脑, 则必定是从一台电脑, 不断左右边界反复横跳开电脑
即方案数为(2^{r-l+1})
当使用题目给的自动开启方案时, 注意到按区间dp的话
([l, k], [k + 2, g], [g + 2, r]) 把([k + 2, g]) 归于左边(([l, k], [k + 2, r]))或右边(([l, g], [g + 2, r])) 都是可行的
但会多算
故我们在扩展范围的时候除了不使用自动开启方案, 则保证扩展的部分([k, r]) 没用使用自动开启
则每次扩展扩展使用一次自动开启([l, k], [k + 2, r]), 去重, 变成了线性dp
(f(i, j)) 表示前(i) 台电脑手动开了 (j) 次,
(f(i, i) = 2^{i - 1})
(f(i + 1 + k, j + k) += f(i, j) * 2^{k - 1} * C_{j + k}^{k})
int mod, f[N][N], qp[N];
int fac[N], facinv[N], inv[N];
int C(int n, int m) { return (ll)fac[n] * facinv[m] % mod * facinv[n - m] % mod; }
void init(int n) {
fac[0] = fac[1] = facinv[0] = facinv[1] = inv[0] = inv[1] = qp[0] = 1; qp[1] = 2;
rep (i, 2, n)
qp[i] = (ll)(qp[i - 1] << 1) % mod,
fac[i] = (ll)fac[i - 1] * i % mod,
inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod,
facinv[i] = (ll)facinv[i - 1] * inv[i] % mod;
}
int main() {
IOS; cin >> n >> mod; init(n);
rep (i, 1, n) f[i][i] = qp[i - 1];
rep (i, 1, n) rep (j, 1, n) rep (k, 1, n - 1 - i)
f[i + k + 1][j + k] = (f[i + k + 1][j + k] + (ll)f[i][j] * qp[k - 1] % mod * C(j + k, k) % mod) % mod;
rep (i, 1, n) m = (m + f[n][i]) % mod; cout << m;
return 0;
}
注意到当 (sum a_i geqslant x imes (n - 1)) 必定有解
可用归纳法和反证法
否则无解
就变成了选编的顺序
毕竟是颗树, 那就dfs完事
到达叶子节点
如果(a_i geqslant x) 则这条边肯定先选择, 并且把多余的 (a_i - x) 水泥贡献给父亲节点
否则 这条边最后选(最后的时候, 多余的水泥会从父亲节点传过来)
ll a[N], s;
int ans[N], l, r, g[N][2];
VI h[N];
bool v[N];
int get(int x, int e) { return g[e][0] ^ g[e][1] ^ x; }
void dfs(int x, int e) {
v[x] = 1;
for (auto &id : h[x]) if (!v[get(x, id)]) dfs(get(x, id), id);
if (!e) return;
if (a[x] >= k) ans[++l] = e, a[get(x, e)] += a[x] - k;
else ans[--r] = e;
}
int main() {
IOS; cin >> n >> m >> k; r = n;
rep (i, 1, n) cin >> a[i], s += a[i];
if (n - 1 > s / k) return cout << "NO
", 0;
rep (i, 1, m) rep (j, 0, 1) cin >> g[i][j], h[g[i][j]].pb(i);
dfs(1, 0); cout << "YES
";
rep (i, 1, n - 1) cout << ans[i] << '
';
return 0;
}