VP Educational Codeforces Round 33 (Rated for Div. 2)
A. Chess For Three
记录观众是哪一个,观众不能是赢家。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int x = 1, y = 2, z = 3;
for (int i = 0; i < n; ++ i) {
if (a[i] == z) {
std::cout << "NO\n";
return;
}
if (a[i] == x) {
std::swap(y, z);
} else {
std::swap(x, z);
}
}
std::cout << "YES\n";
}
B. Beautiful Divisors
枚举。
点击查看代码
void solve() {
int n;
std::cin >> n;
for (int i = n; i >= 1; -- i) {
for (int j = 0; j < 10; ++ j) {
if ((((1 << j) - 1) << (j - 1)) == i && n % i == 0) {
std::cout << i << "\n";
return;
}
}
}
}
C. Rumor
题意:求每个联通块的最小值的和。
dfs求联通块即可。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> st(n);
int min = 2e9;
auto dfs = [&](auto & self, int u) -> void {
min = std::min(min, a[u]);
st[u] = 1;
for (auto & v : adj[u]) {
if (!st[v]) {
self(self, v);
}
}
};
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
if (!st[i]) {
min = 2e9;
dfs(dfs, i);
ans += min;
}
}
std::cout << ans << "\n";
}
D. Credit Card
题意:一开始你有\(0\)元,每天会增加一些钱或者减少一些钱,或者询问当天的余额。你可以在任意一天充任意多的钱,你要保证任何时候余额都不超过\(d\),同时查询余额的时候余额不是负数。要求充钱次数最少。
如果我们需要充钱,那么最小把余额充到\(0\),最多充到\(d\)。那么我们可以维护一个上界和一个下界,每次查询的时候,如果需要充钱则把下界变为\(0\),上界变为\(d\)。其它情况就是两个值同时变化,如果上界大于\(d\),就变回\(d\),相当于我们在之前充钱的那一天少充一些,如果下界大于\(d\),则无解。
点击查看代码
void solve() {
int n, d;
std::cin >> n >> d;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
int ans = 0;
i64 l = 0, r = 0;
for (auto & x : a) {
if (x == 0) {
if (l < 0) {
l = 0;
}
if (r < 0) {
r = d;
++ ans;
}
} else {
l += x;
r += x;
if (l > d) {
std::cout << -1 << "\n";
return;
}
if (r > d) {
r = d;
}
}
}
std::cout << ans << "\n";
}
E. Counting Arrays
题意:求长度为\(y\),乘积为\(x\)的序列有多少个,数组可以有负数。首先一个结论是,一个数的质因子不超过\(log_2n\)个。那么这个序列其它位置都是\(1\)。那么我们先对\(x\)质因数分解,那么对于每个因子,我们要把它们放到\(y\)个位置,每个位置可以为空,那么这就是插板法了。总共有\(C(cnt + y - 1, y - 1)\)种放法。每个质因子要单独考虑,因为不同质因子是不同的,而插板法要求每个元素没有区间。
然后考虑负数怎么放,显然\(x\)是正数,那么负数个数得是偶数个,那么对于\(y\)个数,我们选偶数个乘上\(-1\),,那么就是\(C(2, y) + C(4, y) + ...\),这个数是\(2^{y-1}\)次方。
代码省略取模类。
点击查看代码
struct Comb {
int n;
std::vector<Z> _fac;
std::vector<Z> _invfac;
std::vector<Z> _inv;
Comb() : n{0}, _fac{1}, _invfac{1}, _inv{0} {}
Comb(int n) : Comb() {
init(n);
}
void init(int m) {
if (m <= n) return;
_fac.resize(m + 1);
_invfac.resize(m + 1);
_inv.resize(m + 1);
for (int i = n + 1; i <= m; i++) {
_fac[i] = _fac[i - 1] * i;
}
_invfac[m] = _fac[m].inv();
for (int i = m; i > n; i--) {
_invfac[i - 1] = _invfac[i] * i;
_inv[i] = _invfac[i] * _fac[i - 1];
}
n = m;
}
Z fac(int m) {
if (m > n) init(2 * m);
return _fac[m];
}
Z invfac(int m) {
if (m > n) init(2 * m);
return _invfac[m];
}
Z inv(int m) {
if (m > n) init(2 * m);
return _inv[m];
}
Z binom(int n, int m) {
if (n < m || m < 0) return 0;
return fac(n) * invfac(m) * invfac(n - m);
}
} comb;
void solve() {
int x, y;
std::cin >> x >> y;
Z ans = 1;
for (int i = 2; i * i <= x; ++ i) {
if (x % i == 0) {
int n = 0;
while (x % i == 0) {
++ n;
x /= i;
}
ans *= comb.binom(n + y - 1, y - 1);
}
}
if (x > 1) {
ans *= comb.binom(y, y - 1);
}
ans *= power<Z>(2, y - 1);
std::cout << ans << "\n";
}
F. Subtree Minimum Query
题意:给你一棵树,每次求\(x\)的子树里跟他距离不超过\(k\)的节点的权值的最小值。强制在线。
可以给每一个深度的点建主席树,然后可持久化。那么问题变成求深度为\([dep_x, dep_x + k]\)的且是\(x\)子树里的节点最小值。可以用\(dfs\)序使得子树变成一个连续的区间。不过似乎最小值没法做差,但发现我们查询的区间就限定了只有子树里的点才能查询到,所以直接单棵树查询就行了。
点击查看代码
#define ls(u) (tr[u].lson)
#define rs(u) (tr[u].rson)
const int N = 1e5 + 5;
struct Node {
int lson, rson;
int min;
}tr[N << 5];
int root[N];
int idx = 0;
void insert(int & u, int v, int l, int r, int p, int val) {
u = ++ idx;
tr[u] = tr[v];
tr[u].min = std::min(tr[u].min, val);
if (l == r) {
return;
}
int mid = l + r >> 1;
if (p <= mid) {
insert(ls(u), ls(v), l, mid, p, val);
} else {
insert(rs(u), rs(v), mid + 1, r, p, val);
}
}
int query(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return tr[u].min;
}
int mid = l + r >> 1;
if (R <= mid) {
return query(ls(u), l, mid, L, R);
} else if (L > mid) {
return query(rs(u), mid + 1, r, L, R);
}
return std::min(query(ls(u), l, mid, L, mid), query(rs(u), mid + 1, r, mid + 1, R));
}
void solve() {
int n, r;
std::cin >> n >> r;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int>> adj(n + 1);
for (int i = 1; i < n; ++ i) {
int u, v;
std::cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> in(n + 1), out(n + 1), dep(n + 1);
std::vector<std::vector<int>> d(n + 1);
int dfn = 0;
auto dfs = [&](auto & self, int u, int fa) -> void {
in[u] = ++ dfn;
d[dep[u]].push_back(u);
for (auto & v : adj[u]) {
if (v == fa) {
continue;
}
dep[v] = dep[u] + 1;
self(self, v, u);
}
out[u] = dfn;
};
dep[r] = 1;
dfs(dfs, r, 0);
int maxd = *std::max_element(dep.begin(), dep.end());
tr[0].min = 2e9;
for (int i = 1, last = 0; i <= maxd; ++ i) {
for (auto & x : d[i]) {
insert(root[i], last, 1, n, in[x], a[x]);
last = root[i];
}
}
int m;
std::cin >> m;
int last = 0;
while (m -- ) {
int x, y;
std::cin >> x >> y;
x = (x + last) % n + 1, y = (y + last) % n;
last = query(root[std::min(maxd, dep[x] + y)], 1, n, in[x], out[x]);
std::cout << last << "\n";
}
}