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 }
View Code

 

 

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

 

F. Upgrading Cities

Unsolved.

posted @ 2018-11-15 08:23  Dup4  阅读(328)  评论(0编辑  收藏  举报