CF1251 的题解
CF1251 的题解
没有错,本小菜鸡又腆着个波脸来写题解了,菜还理直气壮。
A题
题面:说有一个键盘坏了,然后坏掉的键按一次会出现两个相应的字母,好的键是按一次只会出现一个相应的字母。
做法:需要判断一个字母连续出现的次数,如果为奇数那么说明这个键是好的。
水题,没错我又觉得这是水题了
B题
题面:给出n个01字符串,然后意思是可以任意交换01的位置,哪怕不是一个字符串的也可以交换。问最多可以转化出多少个回文串。
做法:首先我们可以想到对于长度为奇数的01字符串来说,他必是一个回文串(您要是能举出反例,我就是粪海狂蛆)。 对于长度为偶数的01字符串来说,如果不是回文串那么说明此时01的个数都是奇数。我们只需要任意换掉一个0或者1就可组成一个回文串了。
我们数出不是回文串的01串的个数尽心两两配对(一个拿1一个拿0)。
如果个数为偶数那么就是最好的情况,答案就是n;如果为奇数,我们就需要考虑有没有长度为奇数的01串,我们和中间那个位置的字符交换,就可以凑成回文串(举个栗子:1000 可以在 10001的帮助下变成 0000 和 10101)。凑不出来答案就是n-1。
多想几组数据还是能想明白的(现场赛你怎么菜的和狗一样?)
C题
题面:给出一个n位的数字,如果两个相邻位置的奇偶性不同,那么就可互换他们的位置。互换次数不限,问最终可以获得的最小数字是多少,可以有前导零。其实就是字符序最小。
做法:遍历一边字符串,然后存下每一个奇数和偶数的位置。然后我们在遍历一边把他们放回去就可以了,放回去的原则是先放小的那个。想想就知道这样的序列是合法的并且是最优的(不会证明,就只能想想)。
1 #include <stdio.h> 2 #include <set> 3 #include <string.h> 4 using namespace std; 5 char s[300009], res[300009]; 6 set <int> :: iterator it; 7 set <int> st[2]; 8 int main() 9 { 10 int T, len; 11 scanf("%d", &T); 12 while(T--) 13 { 14 scanf("%s", s+1); 15 len = strlen(s+1); 16 st[0].clear(); st[1].clear(); 17 for(int i = 1; i <= len; i++) 18 st[(s[i]-'0')%2].insert(i); 19 20 for(int i = 1; i <= len; i++) 21 { 22 if(st[0].empty()) 23 { 24 res[i] = s[*st[1].begin()]; 25 st[1].erase(st[1].begin()); 26 continue; 27 } 28 if(st[1].empty()) 29 { 30 res[i] = s[*st[0].begin()]; 31 st[0].erase(st[0].begin()); 32 continue; 33 } 34 35 if(s[*st[0].begin()] < s[*st[1].begin()]) 36 { 37 res[i] = s[*st[0].begin()]; 38 st[0].erase(st[0].begin()); 39 }else{ 40 res[i] = s[*st[1].begin()]; 41 st[1].erase(st[1].begin()); 42 } 43 } 44 res[len+1] = '\0'; 45 46 printf("%s\n", res+1); 47 } 48 }
D题
题面:说有一个老板要给员工发工资了,每个员工的有一个工资的范围Li和Ri,老板只有S那么多的钱,问怎么发工资才能让所有人工资的中文数最大。
做法:看到求最大值,然后正向思考发现没有好的贪心策略,那么就可以考虑一手二分。二分的板子一敲,然后问题来了,怎么check。
比如现在要check X这个值
那么对于所有Li > X的员工来说只能是在中位数的右边,对于Ri < X的员工来说只能是在中位数的左边,对于这两种情况最小花费的增加值都是Li(手算模拟就可以想明白)。其他的就是放哪里都无所谓,可以最优分配。
先可以数一下必然放左右位置的各有多少,如果其中任意值大于n/2 那么说明是不行的。然后我们把随意放的按Li从小到达排个序,如果左边还没满那么就对所需费用加Li,满了就加X。最后判断费用是否小于老板有的钱就可以。
但是这里有一个问题,二分要求的是在check的过程中,趋势只有一个,就是大于一个值就都行或者都不行,这样我们才可以二分。但是在这题里面如果X小了或者大了都会出现这样的情况,所以我们需要去掉一个。题目告诉我们,老板最起码可以负担一个最低工资,我们对最低工资排个序,那么二分的下界就是第n/2 + 1个人的 L 。然后就可以很愉快的二分了。
1 #include <stdio.h> 2 #include <cstring> 3 #include <vector> 4 #include <set> 5 #include <algorithm> 6 using namespace std; 7 struct node 8 { 9 int l, r; 10 bool operator < (struct node a) 11 { 12 return l < a.l; 13 } 14 }a[200009]; 15 typedef long long ll; 16 int T, n, l, r, mid; 17 ll sum; 18 bool check(int x) 19 { 20 vector <struct node> ve; 21 ve.clear(); 22 int num = 0; 23 ll res = 0; 24 for(int i = 1; i <= n; i++) 25 if(a[i].r<x) 26 { 27 res += a[i].l; 28 num++; 29 } 30 else if(a[i].l>x) 31 { 32 res += a[i].l; 33 }else ve.push_back(a[i]); 34 35 if(num>=n/2+1) return false; 36 37 int size = ve.size(); 38 39 for(int i = 0; i < size; i++) 40 if(num<n/2) 41 { 42 res += ve[i].l; 43 num++; 44 }else res += x; 45 46 return res <= sum; 47 } 48 49 int main() 50 { 51 scanf("%d", &T); 52 while(T--) 53 { 54 scanf("%d %lld", &n, &sum); 55 for(int i = 1; i <= n; i++) 56 scanf("%d %d", &a[i].l, &a[i].r); 57 58 r = 1e9; 59 60 sort(a+1, a+1+n); 61 l = a[n/2+1].l; 62 // printf("%d %d\n", l, r); 63 while(l<=r) 64 { 65 mid = (l+r) / 2; 66 if(check(mid)) 67 { 68 if(!check(mid+1)) break; 69 l = mid+1; 70 }else r = mid - 1; 71 } 72 73 printf("%d\n", mid); 74 } 75 }
E题
题面:有n个选民每个人有一个Mi和Pi,Mi表示如果此时已经有那么多人投了你一票,那么他就会跟风投你一票,Pi表示你可以花那么多钱来买下他手里的选票。问最少要多少钱,才能让大家都投你一票。
做法:对于Mi==0的人来说,他们的选票就是白给的,可以直接拿走,白嫖他。把剩下的人按Mi从小到大排序。然后从后往前遍历,如果现在已经获得的选票+[ i - 1(表示前面的人都把票给你了)] 大于等于Mi那么说明这个人是不需要花钱的,否则的话我们就收买p最小的的选民(包括i在内的已有的选民),可以用multiset或者priority_queue。
1 #include <stdio.h> 2 #include <algorithm> 3 #include <set> 4 using namespace std; 5 struct node 6 { 7 int m, p; 8 bool operator < (struct node a) 9 { 10 if(m==a.m) return p > a.p; 11 return m < a.m; 12 } 13 }a[200009], tmp; 14 int main() 15 { 16 int T, n, pos, num; 17 long long res; 18 scanf("%d", &T); 19 multiset <int> st; 20 while(T--) 21 { 22 res = pos = num = 0; 23 scanf("%d", &n); 24 for(int i = 1; i <= n; i++) 25 { 26 scanf("%d %d", &tmp.m, &tmp.p); 27 if(tmp.m!=0) a[++pos] = tmp; 28 else num++; 29 } 30 sort(a+1, a+1+pos); 31 st.clear(); 32 for(int i = pos; i >= 1; i--) 33 { 34 st.insert(a[i].p); 35 if(num + i - 1 >= a[i].m) continue; 36 num++; 37 res += *st.begin(); 38 st.erase(st.begin()); 39 } 40 41 printf("%lld\n", res); 42 } 43 }