Educational Codeforces Round 58 Solution

A. Minimum Integer

签到。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 ll l, r, d;
 6 
 7 int main()
 8 {
 9     int t; scanf("%d", &t);
10     while (t--)
11     {
12         scanf("%lld%lld%lld", &l, &r, &d);
13         if (d < l) printf("%lld
", d);
14         else printf("%lld
", ((r / d) + 1) * d);
15     }
16     return 0;
17 }
View Code

B. Accordion

签到。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 5000010
 5 char s[N];
 6 
 7 int work()
 8 {
 9     int l = -1, r = -1, flag, len = strlen(s + 1);
10     flag = false;
11     for (int i = 1; i <= len; ++i) 
12     {
13         if (s[i] == '[') flag = true; 
14         else if (s[i] == ':' && flag) 
15         {
16             l = i;
17             break;
18         }
19     } 
20     if (l == -1) return -1;
21     flag = false;
22     for (int i = len; i >= 1; --i)
23     {
24         if (s[i] == ']') flag = true;
25         else if (s[i] == ':' && flag)
26         {
27             r = i;
28             break;
29         }
30     }
31     if (r == -1) return -1;
32     if (r <= l) return -1;
33     int res = 4;
34     for (int i = l + 1; i < r; ++i) if (s[i] == '|')
35         ++res;
36     return res;
37 }
38 
39 int main()
40 {
41     while (scanf("%s", s + 1) != EOF)
42         printf("%d
", work());
43     return 0;
44 }
View Code

C. Division and Union

Solved.

题意:

有n个区间,将它分成两个集合,使得每个集合任意出一个区间组成的一对,这对没有交

思路:

按左端点排序,如果存在这样的划分,那么必定一个界限使得当前区间与之前的那个区间没有交,这样的话,后面的区间和之前的区间都不会有交

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100010
 5 int t, n, ans[N];
 6 struct node
 7 {
 8     int l, r, id;
 9     void scan(int id) { scanf("%d%d", &l, &r); this->id = id; } 
10     bool operator < (const node &other) const { return l < other.l || (l == other.l && r < other.r); }
11 }a[N];
12 
13 int main()
14 {
15     scanf("%d", &t);
16     while (t--)
17     {
18         scanf("%d", &n); 
19         for (int i = 1; i <= n; ++i) a[i].scan(i);
20         sort(a + 1, a + 1 + n);
21         int pos = -1; int maxr = a[1].r;
22         for (int i = 2; i <= n; ++i)
23         {
24             if (a[i].l > maxr)
25             {
26                 pos = i;
27                 break; 
28             }
29             maxr = max(maxr, a[i].r);
30         }
31         if (pos == -1) puts("-1");
32         else 
33         {
34             for (int i = 1; i <= n; ++i) ans[a[i].id] = i < pos ? 2 : 1;
35             for (int i = 1; i <= n; ++i) printf("%d%c", ans[i], " 
"[i == n]);
36         }    
37     }
38     return 0;
39 }
View Code

D. GCD Counting

Upsolved.

题意:

一棵树中,每个点有权值,找出一条最长的简单路径,使得这个路劲上所有点的点权的gcd > 1

思路:

枚举质因子,再在虚树上dp

质因子很多,有1w多个,但是我们考虑每个质因子对应的集合的总和不会太多,

因为一个数的质因子个数不会太多,2e5一下也就十几个,那么一个数的贡献也就十几个

最后的总和就是O(nlogn)级别的

其实不用建虚树,直接在dfs序上dp就可以了

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 200010
 5 int n, a[N], res;
 6 vector <int> G[N];
 7 vector <int> fac[N];  
 8 
 9 int fa[N], p[N], pos[N], cnt; int f[2][N]; 
10 void pre(int u)
11 {
12     p[u] = ++cnt;
13     for (auto v : G[u]) if (v != fa[u])
14     {
15         fa[v] = u;
16         pre(v);
17     }
18 }
19 
20 void init()
21 {
22     for (int i = 1; i <= n; ++i) G[i].clear();
23     for (int i = 2; i < N; ++i) fac[i].clear();    
24     memset(pos, -1, sizeof pos);
25     res = 0; cnt = 0;
26 }
27 
28 int main()
29 {
30     while (scanf("%d", &n) != EOF)
31     {
32         init();
33         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
34         for (int i = 1, u, v; i < n; ++i)
35         {
36             scanf("%d%d", &u, &v);
37             G[u].push_back(v);
38             G[v].push_back(u);
39         }
40         pre(1);
41         for (int i = 1; i <= n; ++i)
42         {
43             int tmp = a[i];
44             int limit = sqrt(tmp);
45             for (int j = 2; j <= limit; ++j)
46             {
47                 if (tmp % j == 0)
48                 {
49                     fac[j].push_back(i);
50                     while (tmp % j == 0) tmp /= j;
51                 }
52             }
53             if (tmp != 1) fac[tmp].push_back(i); 
54         }
55         for (int i = 2; i < N; ++i) if (fac[i].size() >= 1)
56         {
57             sort(fac[i].begin(), fac[i].end(), [](int x, int y) { return p[x] > p[y]; }); 
58             int len = fac[i].size(); 
59             for (int j = 0; j < len; ++j) for (int o = 0; o < 2; ++o) f[o][j] = 0;
60             for (int j = 0; j < len; ++j) pos[fac[i][j]] = j;
61             for (int j = 0, u, v; j < len; ++j) 
62             {
63                 v = fac[i][j];
64                 res = max(res, f[0][j] + f[1][j] + 1);
65                 if (pos[fa[v]] != -1) 
66                 {
67                     int id = pos[fa[v]];
68                     if (f[0][j] + 1 >= f[0][id])
69                     {
70                         f[1][id] = f[0][id];
71                         f[0][id] = f[0][j] + 1;
72                     }
73                     else if (f[0][j] + 1 >= f[1][id])
74                     {
75                         f[1][id] = f[0][j] + 1;
76                     }
77                 }
78             }
79             for (int j = 0; j < len; ++j) pos[fac[i][j]] = -1;
80         }
81         printf("%d
", res);
82     }
83     return 0;
84 }
View Code

E. Polycarp's New Job

签到.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 500010
 5 int n, x, y, l, r; char op[10];
 6 
 7 int main()
 8 {
 9     l = 0, r = 0;
10     scanf("%d", &n);
11     while (n--)
12     {
13         scanf("%s%d%d", op, &x, &y);
14         if (x > y) swap(x, y);
15         if (op[0] == '+')
16         {
17             l = max(l, x);
18             r = max(r, y);
19         }
20         else
21         {
22             puts(l <= x && r <= y ? "YES" : "NO");
23         }
24     }
25     return 0;
26 }
View Code

F. Trucks and Cities

Upsolved.

题意:

$有n个城市,有m辆卡车需要从s_i -> f_i 每公里耗油c_i升,最多加油r_i次$

$求最小的油箱体积,使得所有卡车都能在加油次数内到达目的地$

思路:

本来考虑二分$但是复杂度是O(n cdot m cdot log(10^{18}))$

考虑$dp$

$dp[i][j][k] 表示从i -> j 最多加油k次的最少油箱容量$

$dp[i][j][k] = min_{w = i}^{j} max(dp[i][w][k - 1], a[j] - a[w])$

我们发现 $dp[i][w][k - 1] 随着w递增而增加,a[j] - a[w] 随着w递增而减少$

$但是,max(dp[i][w][k - 1], a[j] -a[w]) 是先减后增的$ 

并且随着$j的右移动,决策点肯定只会向右移动,而不会回到左边$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 401
 6 int n, m, a[N];
 7 int f[N][N][N];
 8 
 9 int main()
10 {
11     while (scanf("%d%d", &n, &m) != EOF)
12     {
13         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
14         memset(f, 0, sizeof f);
15         for (int i = 1; i <= n; ++i)
16             for (int j = 1; j <= n; ++j)
17                 f[i][j][0] = a[j] - a[i];
18         for (int k = 1; k <= n; ++k)
19             for (int i = 1, w; i <= n; ++i)
20             {
21                 w = i;
22                 for (int j = i + 1; j <= n; ++j) 
23                 {
24                     while (w < j && max(f[i][w + 1][k - 1], a[j] - a[w + 1]) <= max(f[i][w][k - 1], a[j] - a[w])) ++w;
25                     f[i][j][k] = max(f[i][w][k - 1], a[j] - a[w]); 
26                 }
27             }
28         ll res = 0;
29         for (int i = 1, s, e, c, r; i <= m; ++i)
30         {
31             scanf("%d%d%d%d", &s, &e, &c, &r);
32             res = max(res, 1ll * c * f[s][e][r]);
33         }
34         printf("%lld
", res);
35     }
36     return 0;
37 }
View Code

G. (Zero XOR Subset)-less

Upsolved.

题意:

将一些数分组,使得没有任意一个这些组的子集的异或和等于0

思路:

如果所有数异或起来等于$0$就是无解的情况

否则就是线性基中基的个数,因为每个基的最高位的$1所在位置不同$

$所以这些基不管怎么异或都不会是0$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 200010
 5 int n, a[N], p[110];
 6 
 7 int main()
 8 {
 9     while (scanf("%d", &n) != EOF)
10     {
11         int t = 0;
12         for (int i = 1; i <= n; ++i) scanf("%d", a + i), t ^= a[i];
13         if (!t) puts("-1");
14         else
15         {
16             memset(p, 0, sizeof p);
17             for (int i = 1; i <= n; ++i)
18                for (int j = 31; j >= 0; --j) 
19                     if ((a[i] >> j) & 1)
20                      {
21                         if (!p[j]) 
22                         {
23                             p[j] = a[i];
24                             break;
25                         }
26                         else a[i] ^= p[j];
27                     }        
28             int res = 0;
29             for (int i = 0; i < 32; ++i) if (p[i]) ++res;
30             printf("%d
", res);
31         }
32     }
33     return 0;
34 }
View Code
原文地址:https://www.cnblogs.com/Dup4/p/10258600.html