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 }
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 }
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 }
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 }
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 }
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 }
G. Petya and Graph
Unsolved.