Codeforces Round #520 (Div. 2) Solution
A. A Prank
Solved.
题意:
给出一串数字,每个数字的范围是$[1, 1000]$,并且这个序列是递增的,求最多擦除掉多少个数字,使得别人一看就知道缺的数字是什么。
思路:
显然,如果缺的这块数字的个数刚好等于右界 - 左界 + 1 那么就可以知道
还需要考虑数据范围,因为是$<= 1000 和 >= 1$ 那么对于两边的边界需要特殊考虑。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 110 5 int n, a[N]; 6 7 int main() 8 { 9 while (scanf("%d", &n) != EOF) 10 { 11 for (int i = 1; i <= n; ++i) scanf("%d", a + i); 12 a[0] = 0; a[n + 1] = 1001; 13 int res = 0, tmp = 1; 14 for (int i = 1; i <= n + 1; ++i) 15 { 16 if (a[i] == a[i - 1] + 1) ++tmp; 17 else 18 { 19 res = max(res, tmp - 2); 20 tmp = 1; 21 } 22 } 23 res = max(res, tmp - 2); 24 printf("%d\n", res); 25 } 26 return 0; 27 }
B. Math
Solved.
题意:
给出一个数$n$, 有两种操作。
第一种是乘上一个任意整数$x$
第二种是开方,并且要保证开方后是整数。
求最少经过多少次操作,可以得到一个最小的数$n$
思路:
考虑$n$的质因数分解形式$a_1^{p_1} \cdot a_2^{p_2} \cdot a_3^{p_3} ...$
那么最小的答案就是$a_1 \cdot a_2 \cdot a_3$
因为一个数开方后是整数的话,显然是每个质因子的幂次都是偶数次,开方相当于每个质因子的幂次 $ / 2$
那么显然每个质因子最小的幂次都是1
再考虑最小的次数,因为乘法可以任意乘一个整数,所以如果需要乘法,那么乘法只需要一次。
再考虑开方,如果质因子中的最高幂次不是2的幂次,那么它不断$ / 2$ 肯定需要至少一次乘法操作,使得可以继续开方下去,直到幂次变为1
那么显然要把幂次变成一个第一个大于等于它的2的幂次数,就可以不断$ / 2$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 2000010 5 int n, Max, res; 6 int Bit[N], pos[N]; 7 vector <int> v; 8 9 int solve(int x) 10 { 11 if (x == 1) return 0; 12 if (pos[x] == 0) return 1 + lower_bound(Bit + 1, Bit + 1 + 20, x) - Bit; 13 for (auto it : v) if (it != x) return 1 + pos[x]; 14 return pos[x]; 15 } 16 17 int main() 18 { 19 Bit[0] = 1; pos[1] = 0; 20 for (int i = 1; i <= 20; ++i) 21 { 22 Bit[i] = Bit[i - 1] << 1; 23 pos[Bit[i]] = i; 24 } 25 while (scanf("%d", &n) != EOF) 26 { 27 if (n == 1) 28 { 29 puts("1 0"); 30 continue; 31 } 32 v.clear(); 33 res = 1; Max = 0; 34 for (int i = 2; i <= n; ++i) 35 { 36 int tmp = 0; 37 while (n % i == 0) 38 { 39 ++tmp; 40 n /= i; 41 } 42 if (tmp) 43 { 44 res *= i; 45 Max = max(Max, tmp); 46 v.push_back(tmp); 47 } 48 } 49 printf("%d %d\n", res, solve(Max)); 50 } 51 return 0; 52 }
C. Banh-mi
Solved.
题意:
给出一个01串,每次询问区间$[l, r]$ 每次可以将区间内一个数取出,并且答案加上这个数的值,区间内剩余的数也加上这个值,求如何安排取出顺序使得答案最大。
思路:
显然1要在0之前取,
我们考虑区间内有$x个1, y 个 0$
那么考虑取出$x个1的答案$
显然 取出的数是形如 1 2 4 8 .....
是一个等比数列,根据求和公式发现答案为$2^x - 1$
再考虑取出的第一个0的数值为 $2^x - 1$
也是一个等比数列,和为 $(2^x - 1) \cdot (2^y - 1)$
合并两项答案 即$2^x \cdot (2^y - 1)$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 const ll MOD = (ll)1e9 + 7; 7 int n, q, a[N]; 8 9 ll qmod(ll base, ll n) 10 { 11 ll res = 1; 12 while (n) 13 { 14 if (n & 1) res = res * base % MOD; 15 base = base * base % MOD; 16 n >>= 1; 17 } 18 return res; 19 } 20 21 int main() 22 { 23 while (scanf("%d%d", &n, &q) != EOF) 24 { 25 a[0] = 0; 26 for (int i = 1; i <= n; ++i) scanf("%1d", a + i), a[i] += a[i - 1]; 27 for (int i = 1, l, r; i <= q; ++i) 28 { 29 scanf("%d%d", &l, &r); 30 int x = a[r] - a[l - 1], y = (r - l + 1) - x; 31 if (x == 0) 32 { 33 puts("0"); 34 continue; 35 } 36 printf("%lld\n", qmod(2, y) * (qmod(2, x) - 1 + MOD) % MOD); 37 } 38 } 39 return 0; 40 }
D. Fun with Integers
Solved.
题意:给出一个数$n$,任意$|a| , |b| < n$ 并且满足 $|a| |b| 互为倍数关系$ 那么答案加上这个倍数,并且这一对$|a|, |b|$不再提供贡献,并且倍数关系$|x| > 1$
思路:
考虑一个数$x$, 那么在$[1, n]$ 中是它的倍数的数一共有 $y = \lfloor \frac {n}{x} \rfloor \cdot 2$ 个 因为还有负的
那么这个数的贡献是$\sum_2 ^y \cdot 2$ 再考虑 $-x$ 也会提供一样的贡献 即答案要乘2
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 int n; 6 7 int main() 8 { 9 while (scanf("%d", &n) != EOF) 10 { 11 ll res = 0; 12 for (int i = 2; i <= n; ++i) 13 { 14 ll x = n / i; 15 res += 2ll * (x + 2) * (x - 1); 16 } 17 printf("%lld\n", res); 18 } 19 return 0; 20 }
E. Company
Upsolved.
题意:
在一棵树中,每次选择一个区间$[l, r]$ 最多删除一个点,使得这个区间内所有点的$lca$ 的深度最大。
思路:
首先有一个点,就是一颗树中一堆点的$LCA$ 其实就是这堆点$DFS序最小 和 最大的两个点的LCA$
不难证明:
$我们约定用st[u] 表示点u的DFS序编号,并且约定 st[x] < st[y] <st[z], $
$根据ST求LCA 显然两个点的LCA是 st[x] 和 st[y] 中间DFS序最小的点,那么再考虑 一个点z $
$ 此处我们再对z求lca ,普通做法显然是 用st[lca(x, y)] - st[z] 之间找一个最小的$
$但实际上没有必要从st[(lca(x, y))] 开始,直接从 st[x] - st[y] 这段中找到的 pos = lca(x, y) 的位置作为左界即可$
$因为考虑 从st[lca(x, y)] - pos 这一段中,不会有DFS序编号比lca(x, y) 还要小的点,因为显然这一段序列都是在lca(x, y) 的子树中$
$如此看来,我们约定用u 表示 [l, r] 中DFS序编号最小的点,v 表示编号最大的点$
$那么LCA 就是 从 st[u] - st[v] 中找一个最小点 即为LCA$
$再考虑移除哪一个点,我们知道答案跟一段序列的最小值有关,我们要让答案有变化,要让这一段序列的区间长度有变化$
$显然是移除左边的一个点或者右边的一个点 即 DFS序编号最大的点 或者 DFS序编号最小的点$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define INF 0x3f3f3f3f 5 #define N 100010 6 int n, q, l, r; 7 vector <int> G[N]; 8 9 int p[N], fp[N], sze[N], son[N], fa[N], deep[N], top[N], cnt; 10 void DFS(int u) 11 { 12 sze[u] = 1; 13 for (auto v : G[u]) if (v != fa[u]) 14 { 15 fa[v] = u; 16 deep[v] = deep[u] + 1; 17 DFS(v); sze[u] += sze[v]; 18 if (!son[u] || sze[v] > sze[son[u]]) son[u] = v; 19 } 20 } 21 22 void getpos(int u, int sp = 1) 23 { 24 top[u] = sp; 25 p[u] = ++cnt; 26 fp[cnt] = u; 27 if (!son[u]) return; 28 getpos(son[u], sp); 29 for (auto v : G[u]) if (v != son[u] && v != fa[u]) 30 getpos(v, v); 31 } 32 33 int querylca(int u, int v) 34 { 35 while (top[u] != top[v]) 36 { 37 if (deep[top[u]] < deep[top[v]]) swap(u, v); 38 u = fa[top[u]]; 39 } 40 if (deep[u] > deep[v]) swap(u, v); 41 return u; 42 } 43 44 struct SEG 45 { 46 struct node 47 { 48 int Max, Min; 49 void init() { Max = 0, Min = INF; } 50 node operator + (const node &r) const 51 { 52 node res; res.init(); 53 res.Max = max(Max, r.Max); 54 res.Min = min(Min, r.Min); 55 return res; 56 } 57 }a[N << 2], res; 58 void build(int id, int l, int r) 59 { 60 a[id].init(); 61 if (l == r) 62 { 63 a[id].Max = a[id].Min = p[l]; 64 return; 65 } 66 int mid = (l + r) >> 1; 67 build(id << 1, l, mid); 68 build(id << 1 | 1, mid + 1, r); 69 a[id] = a[id << 1] + a[id << 1 | 1]; 70 } 71 void query(int id, int l, int r, int ql, int qr) 72 { 73 if (l >= ql && r <= qr) 74 { 75 res = res + a[id]; 76 return; 77 } 78 int mid = (l + r) >> 1; 79 if (ql <= mid) query(id << 1, l, mid, ql, qr); 80 if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr); 81 } 82 }seg; 83 84 int lca(int l, int r) 85 { 86 seg.res.init(); 87 seg.query(1, 1, n, l, r); 88 return querylca(fp[seg.res.Min], fp[seg.res.Max]); 89 } 90 91 int work(int x) 92 { 93 if (x == l) return deep[lca(l + 1, r)]; 94 if (x == r) return deep[lca(l, r - 1)]; 95 return deep[querylca(lca(l, x - 1), lca(x + 1, r))]; 96 } 97 98 void init() 99 { 100 for (int i = 1; i <= n; ++i) G[i].clear(); 101 memset(son, 0, sizeof son); 102 cnt = 0; fa[1] = 0; deep[1] = 0; 103 } 104 105 int main() 106 { 107 while (scanf("%d%d", &n, &q) != EOF) 108 { 109 init(); 110 for (int u = 2, v; u <= n; ++u) 111 { 112 scanf("%d", &v); 113 G[u].push_back(v); 114 G[v].push_back(u); 115 } 116 DFS(1); getpos(1); seg.build(1, 1, n); 117 //for (int i = 1; i <= n; ++i) printf("%d %d %d\n", i, p[i], fp[i]); 118 for (int qq = 1; qq <= q; ++qq) 119 { 120 scanf("%d%d", &l, &r); 121 seg.res.init(); seg.query(1, 1, n, l, r); 122 int x = fp[seg.res.Max], y = fp[seg.res.Min]; 123 int deepx = work(x), deepy = work(y); 124 if (deepx > deepy) printf("%d %d\n", x, deepx); 125 else printf("%d %d\n", y, deepy); 126 } 127 } 128 return 0; 129 }
F. Upgrading Cities
Unsolved.