Codeforces Round #577 (Div. 2)
题意: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 }
题意:给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 }
题意:给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 }
个人觉得难度不止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 }
这份代码其实是有点问题的,如果中间采取插桩法,多次运行,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 }
(E和F本菜鸡肯定写不出)
(如有问题,请在评论区和我说)