Codeforces Round #577 (Div. 2)

A - Important Exam

题意:n个人,m道题,每道题有A B C D E五个选项,每个人在每道题都有一个选项,每道题有一个分值,问所有人的分值总和最多为多少。

分析:对某道题,他的分值是固定的,总共有n个人作答,挑选5个选项中选的人最多的作为答案,可得到分数最多。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <iostream>
 5  
 6 #define maxn 1000005
 7 #define mod 1000000007
 8 #define inf 0x3f3f3f3f
 9 #define start ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
10 #define ll long long
11 using namespace std;
12 string a[1005];
13 ll b[1005][10];
14  
15 int main() {
16     start;
17     int n, m;
18     cin >> n >> m;
19     for (int i = 1; i <= n; ++i) {
20         cin >> a[i];
21         for (int j = 0; j < m; ++j)
22             b[j][a[i][j] - 'A']++;
23     }
24     ll ans = 0;
25     for (int i = 0; i < m; ++i) {
26         int x;
27         cin >> x;
28         ll maxt = 0;
29         for (int j = 0; j < 5; ++j)
30             maxt = max(maxt, b[i][j]);
31         ans += maxt * x;
32     }
33     cout << ans << endl;
34     return 0;
35 }
View Code

B - Zero Array

题意:给n个数,每次挑不同的两个数同时减一,问数组能否全变为0。

分析:如果数总和为奇数,显然不行。如果最大的数比其他所有数加起来都大,显然不行。其余我们分析:总和为偶数,对于数组,我们任意挑两个,并保证挑选均匀(每次挑选最大的和最小的,各减一),即可成立。(我也不会证,但感觉上是对的)。

 1 #include <bits/stdc++.h>
 2  
 3 #define maxn 200005
 4 #define mod 1000000007
 5 #define inf 0x3f3f3f3f
 6 #define start ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 7 #define ll long long
 8 using namespace std;
 9 #define int ll
10  
11 signed main() {
12     start;
13     int n;
14     ll ans = 0;
15     ll maxt = 0;
16     cin >> n;
17     for (int i = 1; i <= n; ++i) {
18         int x;
19         cin >> x;
20         ans += x;
21         maxt = max(maxt, x);
22     }
23     if (ans & 1 || maxt * 2 > ans)
24         cout << "NO" << endl;
25     else
26         cout << "YES" << endl;
27     return 0;
28 }
View Code

C - Maximum Median

题意:给n(n为奇数)个数,和k个加一的机会,问操作后数组中位数是多少。

分析:排序后,对于第n/2+1之前的数,肯定是不进行加一操作了,否则这个加一操作给到后n/2+1个数肯定更有效。令mid=n/2+1,要保证a[mid]越来越大(如果后n/2个数越来越大,答案仍然是a[mid],操作无效)。

第一次操作,将a[mid]加到a[mid+1](如果加不到则将k全部加在a[mid]上)。

第二次操作,将a[mid]和a[mid+1]加到a[mid+2](如果加不到则将剩余的k均匀分到a[mid],a[mid]上)。

……

第n/2次操作,将a[mid],a[mid+1],……,a[n-1]加到a[n](如果加不到则将剩余的k均匀分到a[mid],a[mid+1],……,a[n-1]上)

第n/2+1次操作,将剩余的k均匀分到a[mid],a[mid+1],……,a[n]上

 1 #include <bits/stdc++.h>
 2 
 3 #define maxn 200005
 4 #define mod 1000000007
 5 #define inf 0x3f3f3f3f
 6 #define start ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 7 #define ll long long
 8 using namespace std;
 9 #define int ll
10 int a[maxn];
11 
12 signed main() {
13     start;
14     int n, k;
15     cin >> n >> k;
16     for (int i = 1; i <= n; ++i)
17         cin >> a[i];
18     sort(a + 1, a + n + 1);
19     int mid = n / 2 + 1;
20     if (a[mid] + k < a[mid + 1]) {
21         cout << a[mid] + k << endl;
22         return 0;
23     }
24     int t = 1;
25     for (int i = 1; mid + i <= n; ++i) {
26         if (k - (a[mid + i] - a[mid]) * i < 0)
27             break;
28         t = i + 1;
29         k -= (a[mid + i] - a[mid]) * i;
30         a[mid] = a[mid + i];
31     }
32     a[mid] += (k / t);
33     cout << a[mid];
34     return 0;
35 }
View Code

D - Treasure Hunting

个人觉得难度不止2000,赛终只有369人AC(我显然不在其中),而前三题难度最大的B却有2520人AC,难度梯度设计不太合理。但这也不是我前三题wa8发的理由,掉分就完事了,承认自己菜。赛后看了看rank 1大神的代码,看了一个小时终于看懂了。

题意:n行m列的方格,有k个宝藏,和q个安全列。从(1,1)出发,只能走上、左、右三个方向。走上时,只能从安全列往上。问将所有宝藏捡完最少需要多少步。

分析:

一、对于第i行,我们是从第j列升上来的,我们只考虑最左边和最右边的宝藏,设最左边的宝藏在第l列,最右边的宝藏在第r列。

二、假设先拿最右边的宝藏(拿左边宝藏的情况见补充),那么拿到最左边的宝藏需要abs(j-l)+abs(l-r)步,再考虑离l最近的两个安全列(如果l左边没有则只考虑右边一个),设为q和w。

三、我们对第i行后的第一个有宝藏的行(第i+1行不一定有宝藏)来说(设为s),我们是从第q列和第w列升上来的。

四、再看第i+1行的l2和r2,假设也先拿最左边的宝藏,如果是从第q列升上来的,我们从第i行刚升上来到最左边的宝藏共要走abs(j-l)+abs(l-r)+s+abs(q-l2)+abs(l2-r2);如果是从w列升上来的,则走abs(j-l)+abs(l-r)+s+abs(w-l2)+abs(l2-r2),我们自然取最小的了。

五、取完之后再考虑李l2最近的两个两个安全列(如果l左边没有则只考虑右边一个),进入第三步。

补充:第一行默认从第一列升上来的,且显然先取右边宝藏。

对于第一步的第j列是从第一行递推上来的,实际上这个j分最多四种情况。

对于第二步和第三步,要同时考虑先拿最左边和最右边的情况,所以一行我们需要考虑最多四列,四列走到后一行的最左边或者最右边各取最小值,再延伸出四列。

大神代码(用个人风格改写了一下):

 1 #include <bits/stdc++.h>
 2 
 3 #define maxn 200005
 4 #define mod 1000000007
 5 #define inf 0x3f3f3f3f3f3f3f3f
 6 #define start ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 7 #define ll long long
 8 using namespace std;
 9 
10 struct node {
11     int x, y;
12 
13     bool operator<(const node &b) const {
14         if (x != b.x)
15             return x < b.x;
16         return y < b.y;
17     }
18 
19     node(int x = 0, int y = 0) : x(x), y(y) {}
20 };
21 
22 vector<node> treasures;
23 vector<int> safe;
24 
25 signed main() {
26     start;
27     int n, m, k, q;
28     cin >> n >> m >> k >> q;
29     for (int i = 0; i < k; ++i) {
30         int x, y;
31         cin >> x >> y;
32         treasures.emplace_back(x, y);
33     }
34     treasures.emplace_back(1, 1);
35     ++k;
36     for (int i = 0; i < q; ++i) {
37         int x;
38         cin >> x;
39         safe.push_back(x);
40     }
41     sort(treasures.begin(), treasures.end());//先按行排列,后按列排列
42     sort(safe.begin(), safe.end());//将安全列排列
43     map<ll, ll> selected_col;//first为上次找宝藏所在列选择的安全列(最多为4),second为这种情况的步数
44     selected_col[1] = 0;//默认第一行是从第1列升上来的
45     ll ans;
46     int current_row = 1;//上一个讨论的行,对于第一行来说不存在
47     for (int i = 0, j = 0; i < k; i = j) {//j为下一列的第一个宝藏的指针
48         while (j < k && treasures[j].x == treasures[i].x)//将j移动到后一行第一个宝藏处
49             ++j;
50         int row = treasures[i].x;//现在讨论的行
51         ll l = treasures[i].y, r = treasures[j - 1].y;//该行最左边和最右边的两个宝藏的列数
52         for (auto &it:selected_col)//&很重要
53             it.second += row - current_row;//每种情况都加上上升需要的必要步数,所以current_row初始化为1
54         current_row = row;//转移
55         map<ll, ll> mint;//first为l和r,second为这种情况上一个宝藏所在列到l或者r的最小步数。
56         mint[l] = mint[r] = inf;
57         for (auto &it:selected_col) {
58             mint[l] = min(mint[l], it.second + abs(it.first - r) + abs(l - r));
59             mint[r] = min(mint[r], it.second + abs(it.first - l) + abs(r - l));
60         }
61         ans = min(mint[l], mint[r]);
62         selected_col.clear();
63         for (auto &it:mint) {
64             /*找到离it.first(只有l或者r)最近的两个宝藏*/
65             //吐槽下这里如果r右边没有安全列,又没有哨兵,会出事
66             int safe_index = lower_bound(safe.begin(), safe.end(), it.first) - safe.begin();
67             int safe_col = safe[safe_index];
68             //cout << safe_col << endl;
69             ll cost = it.second + abs(it.first - safe_col);
70             if (selected_col.find(safe_col) == selected_col.end() || cost < selected_col[safe_col])
71                 selected_col[safe_col] = cost;
72             if (safe_index > 0) {
73                 --safe_index;
74                 safe_col = safe[safe_index];
75                 cost = it.second + abs(it.first - safe_col);
76                 if (selected_col.find(safe_col) == selected_col.end() || cost < selected_col[safe_col])
77                     selected_col[safe_col] = cost;
78             }
79         }
80     }
81     cout << ans << endl;
82     return 0;
83 }
View Code

这份代码其实是有点问题的,如果中间采取插桩法,多次运行,safe[safe_index]的随机性可能会导致答案出错。(这是我改了下样例2得到的数据,某一次的运行结果)

大神本身代码也是这样的,不信可以去看看。(但这并不改变人家是大神的事实)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

个人理解:每一行仅仅需要维护最左和最右两个宝藏。同时将小错误修正,得到一份相似代码。

 1 #include <bits/stdc++.h>
 2 
 3 #define maxn 200005
 4 #define mod 1000000007
 5 #define inf 0x3f3f3f3f3f3f3f3f
 6 #define start ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
 7 #define ll long long
 8 using namespace std;
 9 int maxx[maxn];
10 int minn[maxn];
11 vector<int> safe;
12 
13 signed main() {
14     start;
15     memset(minn, 0x3f, sizeof(minn));
16     minn[1] = maxx[1] = 1;
17     int n, m, k, q;
18     cin >> n >> m >> k >> q;
19     for (int i = 0; i < k; ++i) {
20         int x, y;
21         cin >> x >> y;
22         maxx[x] = max(maxx[x], y);
23         minn[x] = min(minn[x], y);
24     }
25     ++k;
26     for (int i = 0; i < q; ++i) {
27         int x;
28         cin >> x;
29         safe.push_back(x);
30     }
31     sort(safe.begin(), safe.end());
32     map<ll, ll> selected_col;
33     selected_col[1] = 0;
34     ll ans;
35     int current_row = 1;
36     for (int row = 1; row <= n; ++row) {
37         if (maxx[row] == 0)
38             continue;
39         ll l = minn[row], r = maxx[row];
40         for (auto &it:selected_col)
41             it.second += row - current_row;
42         current_row = row;
43         map<ll, ll> mint;
44         mint[l] = mint[r] = inf;
45         for (auto &it:selected_col) {
46             mint[l] = min(mint[l], it.second + abs(it.first - r) + abs(l - r));
47             mint[r] = min(mint[r], it.second + abs(it.first - l) + abs(r - l));
48         }
49         ans = min(mint[l], mint[r]);
50         selected_col.clear();
51         for (auto &it:mint) {
52             int safe_index = lower_bound(safe.begin(), safe.end(), it.first) - safe.begin();
53             if(safe_index!=safe.size()) {
54                 int safe_col = safe[safe_index];
55                 ll cost = it.second + abs(it.first - safe_col);
56                 if (selected_col.find(safe_col) == selected_col.end() || cost < selected_col[safe_col])
57                     selected_col[safe_col] = cost;
58             }
59             if (safe_index > 0) {
60                 --safe_index;
61                 int safe_col = safe[safe_index];
62                 ll cost = it.second + abs(it.first - safe_col);
63                 if (selected_col.find(safe_col) == selected_col.end() || cost < selected_col[safe_col])
64                     selected_col[safe_col] = cost;
65             }
66         }
67     }
68     cout << ans << endl;
69     return 0;
70 }
View Code

(E和F本菜鸡肯定写不出)

(如有问题,请在评论区和我说)

posted @ 2019-08-06 03:14  幕无  阅读(203)  评论(0编辑  收藏  举报
1 博文导航目录