Educational Codeforces Round 55 (Rated for Div. 2) Solution

A. Vasya and Book

Solved.

三种方式取$Min$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define INF 0x3f3f3f3f3f3f3f3f
 6 int t;
 7 ll n, x, y, d;
 8 
 9 ll calc(ll x)
10 {
11     return x % d == 0 ? x / d : x / d + 1;
12 }
13 
14 int main()
15 {
16     scanf("%d", &t);
17     while (t--)
18     {
19         scanf("%lld%lld%lld%lld", &n, &x, &y, &d);
20         ll res = INF;
21         if ((abs(y - x)) % d == 0) res = min(res, (abs(y - x) / d));
22         if ((y - 1) % d == 0) res = min(res, calc(x) + (y - 1) / d);
23         if ((n - y) % d == 0) res = min(res, calc(n - x) + (n - y) / d);
24         if (res == INF) res = -1;
25         printf("%lld\n", res);
26     }
27     return 0;
28 }
View Code

 

B. Vova and Trophies

Solved.

合并相同种类,再判断是否有单独的S使得替换掉它合并后得到更优解

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100010
 5 #define pii pair <int, int> 
 6 int n, m;
 7 char s[N];
 8 pii a[N]; 
 9 
10 int main()
11 {
12     while (scanf("%d", &n) != EOF)
13     {
14         scanf("%s", s + 1);
15         int tot = s[1] == 'G', m = 0, tmp = 1;
16         for (int i = 2; i <= n; ++i)
17         {
18             tot += s[i] == 'G';
19             if (s[i] != s[i - 1])
20             {
21                 a[++m] = pii(tmp, s[i] == 'G'); 
22                 tmp = 0; 
23             }
24             ++tmp;
25         }
26         a[++m] = pii(tmp, s[n] != 'G');
27         int res = 0;
28         for (int i = 1; i <= m; ++i)
29         {
30             if (a[i].second == 0) 
31                 res = max(res, min(tot, a[i].first + 1));
32             else if (i != 1 && i != m && a[i].first == 1)
33                 res = max(res, min(tot, a[i - 1].first + 1 + a[i + 1].first));
34         }
35         printf("%d\n", res);
36     }
37     return 0;
38 }
View Code

 

C. Multi-Subject Competition

Solved.

题意:

有一个多学科竞赛,一名队员只会一门学科

派出去的队伍对于每个要参加的学科其参加的人数要相等

并且每个队员对于其会的那门学科有一个技能值

求如何派出队伍参加学科使得队伍中所有队员的技能值总和最大。

思路:

枚举要参加的学科派出的队员数,网上枚举,不符合条件的学科消除影响,每名队员只会遍历一次

时间复杂度$O(n)$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100010
 5 int n, m;
 6 vector <int> v[N];
 7 int tot[N];
 8 queue <int> q;
 9 
10 int main()
11 {
12     while (scanf("%d%d", &n, &m) != EOF)
13     {
14         for (int i = 1; i <= m; ++i) v[i].clear(); 
15         memset(tot, 0, sizeof tot);
16         while (!q.empty()) q.pop();
17         for (int i = 1, s, r; i <= n; ++i)
18         {
19             scanf("%d%d", &s, &r);
20             v[s].push_back(r);
21         }
22         for (int i = 1; i <= m; ++i) sort(v[i].rbegin(), v[i].rend());
23         for (int i = 1; i <= m; ++i) q.push(i); 
24         int res = 0, tmp = 0;
25         for (int i = 1; ; ++i)
26         {
27             if (q.empty()) break;
28             for (int j = 1, len = q.size(); j <= len; ++j)
29             {
30                 int top = q.front(); q.pop();
31                 tmp -= tot[top]; 
32                 if (v[top].size() < i) 
33                     continue;
34                 tot[top] += v[top][i - 1];
35                 if (tot[top] <= 0) continue;
36                 tmp += tot[top];    
37                 q.push(top);
38             }
39             res = max(res, tmp);
40         }
41         printf("%d\n", res);
42     }
43     return 0; 
44 }
View Code

 

D. Maximum Diameter Graph

Solved.

题意:

给出一个n,和每个点最大的度数,构造一棵树,使得每个点的度数不超过最大度数,并且直径最长

思路:

如果所有点的度数之和小于$2 * (n - 1)$ 那么就不可以构造

否则将所有度数$>= 2的点放到直径上,再两边添加一个度为1的点(如果有)$

$剩下的点就连到这条直径上还有度数剩余的点上$

 

n给的好小,好怀疑自己的做法,当时。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 510
 5 int n, a[N];
 6 vector <int> l, r;
 7 
 8 int main()
 9 {
10     while (scanf("%d", &n) != EOF)
11     {
12         int sum = 0;
13         l.clear(), r.clear();
14         for (int i = 1; i <= n; ++i) 
15         {
16             scanf("%d", a + i);
17             sum += a[i];
18             if (a[i] > 1) l.push_back(i);
19             else r.push_back(i); 
20         }
21         if (sum < 2 * (n - 1)) puts("NO");
22         else
23         {
24             if (!r.empty()) l.insert(l.begin(), r.end()[-1]), r.pop_back();
25             if (!r.empty()) l.push_back(r.end()[-1]), r.pop_back();
26             printf("YES %d\n", (int)l.size() - 1);
27             printf("%d\n", n - 1);
28             for (int i = 1, len = l.size(); i < len; ++i) printf("%d %d\n", l[i], l[i - 1]), --a[l[i]], --a[l[i- 1]]; 
29             for (int i = 0, j = 0, len1 = l.size(), len2 = r.size(); i < len2; ++i)
30             {
31                 while (a[l[j]] == 0) ++j;
32                 --a[l[j]];
33                 printf("%d %d\n", r[i], l[j]);
34             }
35         }
36     }
37     return 0;
38 }
View Code

 

E. Increasing Frequency

Solved.

题意:

给出一个n个数,可以选取一段$[l, r],$ 使得区间内所有数都$+k$

求最多进行一次这样的操作,使得最后$a_i == c$的个数最多

求这个最多的个数

思路:

显然一个区间的贡献这个区间内相同数字最大的个数加上区间外c的个数,然后这样区间枚举就是$O(n^2)$

我们可以考虑,枚举$a_i$ 即要把哪些数字变成$c$

那么这时候,所有不是$c也不是a_i 的数字就没有用$ 不妨把它们单独拿出来考虑(类似于虚树思想)

假设$L[i], R[i] 表i左边的c的个数 和 i 右边的c的个数$

那么我假设序列中有$x个a(a 为枚举的a_i)$

$L[1], R[1], ...  L[x], R[x]$

我们$需要找一个以后 x, y 使得 R[y] + (y - x + 1) + L[x] 最大$

$那么可以枚举x ,然后即找一个最大的 (y - x + 1) + R[y]$ 

这个可以用线段树维护。

发现常数项是递增的,建树的时候就可以给它,每次更新的时候,相当于后面的每一个都减一

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 500010
 5 int n, c, a[N], dpl[N], dpr[N];
 6 vector <int> v[N];
 7 
 8 namespace SEG
 9 {
10     int Max[N << 2], lazy[N << 2];
11     void pushup(int id) { Max[id] = max(Max[id << 1], Max[id << 1 | 1]); }
12     void build(int now, int id, int l, int r)
13     {
14         Max[id] = lazy[id] = 0;
15         if (l == r)
16         {
17             Max[id] = l + dpr[v[now][l - 1]];
18             return;
19         }
20         int mid = (l + r) >> 1;
21         build(now, id << 1, l, mid);
22         build(now, id << 1 | 1, mid + 1, r);
23         pushup(id);
24     }
25     void pushdown(int id)
26     {
27         if (!lazy[id]) return;
28         lazy[id << 1] += lazy[id];
29         Max[id << 1] += lazy[id];
30         lazy[id << 1 | 1] += lazy[id];
31         Max[id << 1 | 1] += lazy[id];
32         lazy[id] = 0;
33     }
34     void update(int id, int l, int r, int ql, int qr)
35     {    
36         if (l >= ql && r <= qr)
37         {
38             --Max[id];
39             --lazy[id];
40             return;
41         }
42         int mid = (l + r) >> 1;
43         pushdown(id);
44         if (ql <= mid) update(id << 1, l, mid, ql, qr);
45         if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr);
46         pushup(id);
47     }
48     int query(int id, int l, int r, int ql, int qr)
49     {
50         if (l >= ql && r <= qr) return Max[id];
51         int mid = (l + r) >> 1;
52         pushdown(id);
53         int res = 0;
54         if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr));
55         if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr));
56         return res;
57     }
58 }
59 
60 void init()
61 {
62     for (int i = 1; i <= 500000; ++i) v[i].clear();
63 }
64 
65 int main()
66 {
67     while (scanf("%d%d", &n, &c) != EOF)
68     {
69         init();
70         for (int i = 1; i <= n; ++i) scanf("%d", a + i), v[a[i]].push_back(i);
71         int tmp = 0;
72         for (int i = 1; i <= n; ++i)
73         {
74             dpl[i] = tmp;
75             tmp += a[i] == c; 
76         }
77         tmp = 0;
78         for (int i = n; i >= 1; --i)
79         {
80             dpr[i] = tmp;
81             tmp += a[i] == c;
82         }
83         int res = 0;
84         for (int i = 1; i <= 500000; ++i) if (v[i].size())
85         {
86             int len = v[i].size();
87             SEG::build(i, 1, 1, len);
88             for (int j = 0; j < len; ++j)
89             {
90                 res = max(res, dpl[v[i][j]] + SEG::query(1, 1, len, j + 1, len));
91                 SEG::update(1, 1, len, j + 1, len);
92             } 
93         }    
94         printf("%d\n", res);
95     }
96     return 0; 
97 }
View Code

 

F. Speed Dial

Upsolved.

题意:

给出一串电话号码,以及需要拨打的次数,有k个快捷键可以快速拨出一串号码,该号码可不在给出的电话号码中

求最少的按键次数(快捷键不算一次按键)

思路:

在$Trie树上DP$

$dp[x][rem][fa] 表示到第x个节点,剩余rem次快捷键使用,上一次使用快捷键的节点为fa$

$dp2[x][rem][fa][i] 前面含义相同,i 表示第i个子节点$

$dp[x][rem][fa] = min(dp[x][rem][fa], dp[x][rem - 1][x])$

$dp2[x][rem][fa][i] = min(dp2[x][rem][fa][i], dp2[x][rem - j][fa][i + 1] + dp[x[i]][j][fa])$

$dp[x][rem][fa] = min(dp[x][rem][fa], dp2[x][rem][fa][0] + cnt * len)$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define INF 0x3f3f3f3f
 5 #define N 510
 6 int n, k, m;
 7 struct TRIE
 8 {
 9     int cnt, deep;
10     int son[10]; 
11     void init()
12     {
13         cnt = 0;
14         memset(son, -1, sizeof son);
15     }
16 }a[N]; int pos;
17 
18 char s[N]; int tot;
19 void insert()
20 {
21     scanf("%s%d", s, &tot);
22     int now = 0;
23     for (int i = 0, len = strlen(s); i < len; ++i)
24     {
25         int to = s[i] - '0'; 
26         if (a[now].son[to] == -1)
27         {
28             a[now].son[to] = ++pos;
29             a[pos].init();
30             a[pos].deep = a[now].deep + 1;
31         }
32         now = a[now].son[to];
33     }
34     a[now].cnt += tot;
35 }
36 
37 int dp[N][15][N];
38 int dp2[N][15][N][15];
39 int DFS(int x, int rem, int fa)
40 {
41     if (dp[x][rem][fa] != -1) return dp[x][rem][fa];
42     dp[x][rem][fa] = INF;
43     if (rem) dp[x][rem][fa] = DFS(x, rem - 1, x);
44     vector <int> G;
45     for (int i = 0; i < 10; ++i) if (a[x].son[i] != -1)
46         G.push_back(a[x].son[i]);
47     dp2[x][rem][fa][G.size()] = 0;
48     for (int i = (int)G.size() - 1; i >= 0; --i)
49     {
50         for (int j = 0; j <= rem; ++j)
51             dp2[x][rem][fa][i] = min(dp2[x][rem][fa][i], dp2[x][rem - j][fa][i + 1] + DFS(G[i], j, fa));
52     }
53     dp[x][rem][fa] = min(dp[x][rem][fa], dp2[x][rem][fa][0] + a[x].cnt * (a[x].deep - a[fa].deep));
54     return dp[x][rem][fa];
55 }
56 
57 void init()
58 {
59     a[0].init();
60     pos = 0;
61     a[0].deep = 0;
62 }
63 
64 int main()
65 {
66     while (scanf("%d%d", &n, &k) != EOF)
67     {
68         init();
69         for (int i = 1; i <= n; ++i) insert();
70         memset(dp, -1, sizeof dp);
71         memset(dp2, 0x3f, sizeof dp2);
72         printf("%d\n", DFS(0, k, 0));    
73     }
74     return 0;
75 }
View Code

 

 

 

G. Petya and Graph

Unsolved.

posted @ 2018-11-30 12:35  Dup4  阅读(181)  评论(0编辑  收藏  举报