小学组 — 近似分析 near
题意
给定 (q) 个小数 (f),对它们分别判断使用「每次四舍五入最后一位」的方法得到的整数,与「直接根据十分位四舍五入」得到的整数是否相同。
(f) 的小数点后的位数不超过 (10^4),且至少有一位,(1 ≤ q ≤ 20),(0 < f < 10^4)。
题解
可以使用类似高精度直接模拟的方法暴力做。
当然也可以像 std 一样直接分类讨论:
- 如果小数点后第一位不是 (4),答案为
YES
。 - 如果小数点后全部都是 (4),答案为
YES
。 - 否则找到小数点后第一个不是 (4) 的数字,如果它不超过 (4),那么答案为
YES
,否则答案为NO
。
#include <bits/stdc++.h>
int main() {
freopen("near.in", "r", stdin);
freopen("near.out", "w", stdout);
int q, pos; std::cin >> q;
while(q--) {
std::string s; std::cin >> s;
for(pos = 0; s[pos] != '.'; pos++);
if(s[++pos] != '4') std::cout << "YES
";
else {
while(pos < s.size() && s[pos] == '4') pos++;
std::cout << (pos == s.size() || s[pos] <= '4' ? "YES
" : "NO
");
}
}
return 0;
}
初中组 — 随机排列 rand
题意
有一个排列,每时每刻将 (0 sim 2^{k} - 1) 按照从小到大的顺序排列。
接下来 (m) 次操作,每次给定两个非负整数 (p, q)。表示先将从小到大的二进制第 (p) 位的大小关系取反(即这一位上的 (0<1) 变成 (1<0),反之亦然),再查询排列中第 (q) 个数字的十进制表示。
(1 ≤ k ≤ 64),(1 ≤ m ≤ 10^5)。
题解
用一个 unsigned long long
的变量 (r) 表示当前被取反的位置有哪些,初始时 (r) 是 (0),每次给定 (p) 后 (r gets r oplus 2^p)((oplus) 表示异或)。
那么,当询问排列中第 (q) 个整数时,直接输出 (r oplus q) 即可。
时间复杂度 (O(m))。
#include <bits/stdc++.h>
typedef unsigned long long ull;
int main() {
freopen("rand.in", "r", stdin);
freopen("rand.out", "w", stdout);
int k, m; ull r = 0; scanf("%d %d", &k, &m);
while(m--) {
int p; ull q; scanf("%d %llu", &p, &q);
printf("%llu
", q ^ (r ^= (1ull << p)));
}
return 0;
}
初中组 — 春色满园 yard
题意
给定一棵包含 (n) 个点的树,有一个长度为 (m) 的操作序列,包含两种操作:、
1 a b c
表示 (a sim b) 的路径上每个点的点权 (+c)。2 a b
表示查询 (a sim b) 的路径上的点的点权和。
但你并不需要做这道题。给定一个参数 (k),你每次需要选择操作序列中相邻两个操作进行交换:
- 两个修改操作不能交换。
- 两个查询操作随意交换。
- 一个修改和一个查询操作只能交换至多 (k) 次。
最大化查询操作的答案之和。
(1 ≤ n ≤ 10^3),(1 ≤ m ≤ 200),(1 ≤ k ≤ 10^9),(1 ≤ c ≤ 100)。
题解
容易发现,如果 (kge m^2),那我们肯定会把所有修改放在前面,所有查询放在后面。
现在既然 (k) 不够用,相当于是把 (k) 次操作分配给每个查询操作。例如一个查询操作被分配了 (x) 次机会,相当于它的位置往后移动了 (x) 个查询操作。
预处理一个数组 (w(i, j)),表示第 (i) 个查询操作与其后共 (j) 个修改操作对答案造成的贡献之和,预处理部分是可以用前缀和做到 (O(m^2 n)) 的。
那么使用背包分配即可,用 (f(i, j)) 表示前 (i) 个查询操作,已经被分配了 (j) 次机会,得到的收益(收益可以提前预处理),转移时枚举给当前查询分配几次机会,即 (f(i, j) = maxlimits_{k} { f(i - 1, j - k) + w(i, k) })。
假设总共有 (q) 个查询操作,那么答案就是 (f(q, k) + { m original}),( m original) 表示原序列不改动时本身的答案。
时间复杂度 (O(m^2 (n + min{k, m^2})))。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 5e3 + 5, M = 205, K = 1e4 + 5;
std::vector<int> G[N];
int n, m, k, opt[M], a[M], b[M], dep[N], fa[N], len[M];
ll c[M], val[M][M], f[M][K], ans, tag[N];
void build(int u) {
dep[u] = dep[fa[u]] + 1;
for(unsigned i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(v == fa[u]) continue;
fa[v] = u; build(v);
}
}
void modify(int u, int v, ll w) {
if(dep[u] < dep[v]) std::swap(u, v);
while(dep[u] > dep[v]) tag[u] += w, u = fa[u];
while(u != v) tag[u] += w, tag[v] += w, u = fa[u], v = fa[v];
tag[u] += w;
}
ll query(int u, int v) {
if(dep[u] < dep[v]) std::swap(u, v);
ll res = 0;
while(dep[u] > dep[v]) res += tag[u], u = fa[u];
while(u != v) res += tag[u], res += tag[v], u = fa[u], v = fa[v];
return res + tag[u];
}
ll get(int x, int y) {
memset(tag, 0, sizeof(tag));
modify(a[x], b[x], 1);
return c[y] * query(a[y], b[y]);
}
int main() {
freopen("yard.in", "r", stdin);
freopen("yard.out", "w", stdout);
scanf("%d %d %d", &n, &m, &k);
for(int i = 1; i < n; i++) {
int u, v; scanf("%d %d", &u, &v);
G[u].push_back(v); G[v].push_back(u);
}
build(1);
for(int i = 1; i <= m; i++) {
scanf("%d %d %d", &opt[i], &a[i], &b[i]);
if(opt[i] == 1) {
scanf("%lld", &c[i]);
modify(a[i], b[i], c[i]);
} else ans += query(a[i], b[i]);
}
int qry = 0, tot = 0;
for(int i = 1; i <= m; i++) if(opt[i] == 2) {
qry++;
for(int j = i + 1; j <= m; j++) if(opt[j] == 1) {
len[qry]++;
val[qry][len[qry]] = val[qry][len[qry] - 1] + get(i, j);
}
tot += len[qry];
}
k = std::min(tot, k);
for(int i = 1; i <= qry; i++)
for(int j = 0; j <= k; j++)
for(int cur = 0; cur <= len[i] && cur <= j; cur++)
f[i][j] = std::max(f[i][j], f[i - 1][j - cur] + val[i][cur]);
printf("%lld
", ans + f[qry][k]);
return 0;
}