VP Codeforces Round 907 (Div. 2)
A. Sorting with Twos
题意:给你一个数组,你每次可以给前\(2^i\)个数减一,问能不能使得数组非递减。
\([2^i, 2^{i + 1} - 1]\)这段数大小关系保持不变,看每段是不是升序关系就行了。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 1, j = 2; i < n; j *= 2) {
for (int k = i; k + 1 < j && k + 1 < n; ++ k) {
if (a[k] > a[k + 1]) {
std::cout << "NO\n";
return;
}
}
i = j;
}
std::cout << "YES\n";
}
B. Deja Vu
题意:\(n\)个数,\(q\)次操作,每次把所有\(2^i\)的倍数都加上\(2^{i-1}\)。问最后每个数的值。
如果一个数是\(2^i\)的倍数,那么加上\(2^{i-1}\)次方后最大就只能是\(2^{i-1}\)的倍数了。记每个数它的最大的\(2\)的整数幂的因子,每次操作把所有\(2^x\)倍数都加到\(2^{x-1}\)里面去就行。每个数最多被操作\(30\)次。
点击查看代码
void solve() {
int n, q;
std::cin >> n >> q;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int> > b(31);
for (int i = 0; i < n; ++ i) {
for (int j = 30; j >= 0; -- j) {
if (a[i] % (1ll << j) == 0) {
b[j].push_back(i);
break;
}
}
}
while (q -- ) {
int x;
std::cin >> x;
for (int i = x; i <= 30; ++ i) {
for (auto & j : b[i]) {
a[j] += 1 << (x - 1);
b[x - 1].push_back(j);
}
b[i].clear();
}
}
for (int i = 0; i < n; ++ i) {
std::cout << a[i] << " \n"[i == n - 1];
}
}
C. Smilo and Monsters
题意:\(n\)个数,你有一个\(x\),每次你可以选择给一个数减一,然后\(x=x+1\);或者让某个大于等于\(x\)的数减去\(x\),然后\(x=0\)。问每个数变成0最少需要几次操作。
考虑模拟,从小到大一个一个减一,\(x\)留着打最大的,最后可能操作到剩下一个数,这时考虑分类讨论。
假设剩下\(m\),如果\(m==1\),答案加上1就好了。否则假设我们还执行\(y\)次减一操作,那么要满足\(x + y <= m - y\), 得\(2y <= m - x\),这里需要向上取整。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end());
int l = 0, r = n - 1;
i64 ans = 0, x = 0;
while (l < r) {
// std::cout << l << " " << r << "\n";
if (x + a[l] >= a[r]) {
ans += a[r] - x;
ans += 1;
a[l] = x + a[l] - a[r];
a[r] = 0;
x = 0;
-- r;
if (a[l] == 0) {
++ l;
}
} else {
x += a[l];
ans += a[l];
++ l;
}
}
if (l == r) {
// std::cout << ans << " " << a[r] << " " << x << "\n";
//x + y <= a[r] - y
//2y <= a[r] - x
if (a[r] == 1) {
ans += 1;
} else {
a[r] -= x;
ans += (a[r] + 1) / 2 + 1;
}
}
std::cout << ans << "\n";
}
D. Suspicious logarithms
题意:\(f(x)\)是最小的\(y\),满足\(2^y <= x\),\(g(x)\)是最小的\(z\)满足\(f(x)^z <= x\)。求\(\sum_{i = l}^{r} g(i)\)。
\([2^i, 2^{i+1} - 1]\)的数的\(f\)值都是\(i\),则可以分段处理。对于每一段,\(x ^ i, x ^ {i+1} - 1\)的\(g\)值都是\(i\),同样分段处理。
点击查看代码
void solve() {
i64 l, r;
std::cin >> l >> r;
Z ans = 0;
while (l <= r) {
i64 f = std::__lg(l);
i64 x = (1ll << (f + 1)) - 1;
// std::cerr << l << " " << x << "\n";
i128 now = 1;
for (i64 i = 0, last = 0; last <= std::min(x, r); now *= f, ++ i) {
if (now <= l) {
continue;
}
i64 L = std::max(l, last), R = std::min<i128>({x, r, now - 1});
// std::cout << L << " " << R << " " << i << "\n";
ans += (Z)(R - L + 1) * (i - 1);
last = R + 1;
}
l = x + 1;
}
std::cout << ans << "\n";
}
F. A Growing Tree
题意:开始有一个节点,每次新增一个节点到节点\(v\),保证形状是一棵树。或者给目前节点\(u\)的整棵子树都加上\(v\)。求最后每个节点的值。
用树状数组维护每个操作时间总共加的值,那么可以离线后跑\(dfs\),每个点访问的时候把给它的每个值加到对应的操作时间上,然后这个点的答案就是它的添加时间到\(a\)的区间和。离开时把这个点增加的值删除即可。
点击查看代码
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, {});
}
void add(int x, T v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] += v;
}
}
T query(int x) {
T res = 0;
for (int i = x; i; i -= i & -i) {
res += tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
void solve() {
int q;
std::cin >> q;
std::vector<std::vector<int>> adj(q + 1);
std::vector<std::vector<std::pair<int, int>>> g(q + 1);
std::vector<int> id(q + 1);
int n = 1;
for (int i = 1; i <= q; ++ i) {
int op, x, y;
std::cin >> op >> x;
if (op == 1) {
-- x;
id[n] = i;
adj[x].push_back(n ++ );
} else {
-- x;
std::cin >> y;
g[x].push_back({i, y});
}
}
Fenwick<i64> tr(q + 1);
std::vector<i64> ans(n);
auto dfs = [&](auto self, int u) -> void {
for (auto & [t, v] : g[u]) {
tr.add(t, v);
}
ans[u] = tr.sum(id[u] + 1, q);
for (auto & v : adj[u]) {
self(self, v);
}
for (auto & [t, v] : g[u]) {
tr.add(t, -v);
}
};
dfs(dfs, 0);
for (int i = 0; i < n; ++ i) {
std::cout << ans[i] << " \n"[i == n - 1];
}
}

浙公网安备 33010602011771号