Codeforces Round #807 (Div. 2)
- A、B、C题题解
A - Mark the Photographer
题意:马克要给2n个人照相,分两排,一排站n人,给出每个人的身高,要求后排要比前排的身高至少高出x个单位,问是否能实现这个要求?
分析:先按照身高排序,对应关系是相同位置前排的身高是ai,后排的身高为ai+x,通过遍历第一排人的身高,得到第二排人的身高,判断是否满足要求。
Code:
1. #include<bits/stdc++.h>
2. using namespace std;
3. typedef long long ll;
4. const int N=1e5+10;
5. int a[N];
6. bool solve(int n,int x)
7. {
8. for (int i=0;i<2*n;i++)
9. cin>>a[i];
10. sort(a,a+2*n);
11. for (int i=0;i<n;i++)
12. {
13. if (a[i+n]-a[i]<x) //一旦发现第二排与第一排高度差小于x就确定不成立
14. return false;
15. }
16. return true;
17. }
18. int main()
19. {
20. int t;
21. cin>>t;
22. while (t--)
23. {
24. int n,x;
25. cin>>n>>x;
26. if (solve(n,x))
27. puts("YES");
28. else
29. puts("NO");
30. }
31. return 0;
32. }
B - Mark the Dust Sweeper
题意:有n个房间,给出每个房间的积灰程度,
马克可以从中选择不为零的元素,给 减一,加一,记作一次操作,
最终要使得除最后一个房间的所有房间积灰程度为零,问最少进行多少次操作?
分析:当把积灰从第i个房间转移到第j个房间时尽可能转移到最远的房间,也就是j要最大,通过右移j指针找出连续不为零的序列进行一次操作,把头元素减一,尾元素的下一个元素加一,直到头元素为零,然后头元素右移一个房间,重复上诉操作直至除最后一个房间都执行了操作。仔细看,这个操作可以将中间为零点元素转化为非零元素从而创造连续非零序列,与我们的直觉策略相符。
Code:
1. #include<bits/stdc++.h>
2. using namespace std;
3. typedef long long ll;
4. const int N=1e5+10;
5. int a[2*N];
6. int solve(int n)
7. {
8. for (int i=0;i<n;i++)
9. cin>>a[i];
10. int cnt=0,j=0;
11. for (int i=0;i<n-1;i++) //最后一个元素不用考虑
12. {
13. while (a[i])
14. {
15. while (a[j]&&j<n) //确定一段连续不为0的序列
16. j++;
17. a[i]-=1; //按照题意进行操作
18. a[j]+=1;
19. cnt++;
20. }
21. }
22. return cnt; //根据上面方法求得的次数已经是最优解
23. }
接B题:
上面的代码是无法AC的,如何简化问题?在上面的分析中可以得出最优的策略是将非零的元素变为非零,如果有一段序列开头为非零元素但中间出现了零元素,就可以将非零元素向零元素移动一个灰尘减少非零元素,原来的非零元素起到桥梁的作用,从而将连续非零序列变长,重复操作可以将整个序列变为非零序列,到这里所需要的操作步骤数就是第一个非零元素右边的零元素的数量,前面的前导零序列不需要操作。现在我们有一座桥梁,桥头是动的,终点是定的,不断借助这座桥将桥头的元素搬到终点,当桥头元素为零时,拆掉桥头,新桥头就是下一个元素,桥头的元素依次过桥,桥的长度也不断减小,直到这座桥的长度为零。这个思想也是与之前不断扩大最长非零序列的想法一致的。最后答案要加上所有非零元素,前面拿出一个非零元素的一个单位到零元素的这个“搭桥”过程是不用计算的,因为桥是走一步拆一步,这个元素最终肯定也是移动到终点的了。最终答案就是去除前导零后的零元素个数加上所有非零元素。
改进后的AC代码:
1. #include<bits/stdc++.h>
2. using namespace std;
3. typedef long long ll;
4. const int N=1e5+10;
5. int a[2*N];
6. ll solve(int n)
7. {
8. int start=-1;
9. ll sum=0;
10. for (int i=0;i<n;i++)
11. {
12. cin>>a[i];
13. if (i<n-1) //最后一个元素不考虑
14. sum+=a[i];
15. if (a[i]&&start==-1) //记录首个非零元素的位置
16. start=i;
17. }
18. if (sum==0)
19. return 0;
20. for (int i=start;i<n-1;i++) //加上出现在非零元素后的零点的数量
21. {
22. if (!a[i])
23. sum++;
24. }
25. return sum;
26. }
27. int main()
28. {
29. ios::sync_with_stdio(false);
30. cin.tie(0);
31. int t;
32. cin>>t;
33. while (t--)
34. {
35. int n;
36. cin>>n;
37. cout<<solve(n)<<'\n';
38. }
39. return 0;
40. }
C - Mark and His Unfinished Essay
题意:对一个给定的字符串进行c次操作,每次操作选择其中的一段将其复制添加到字符串的末尾,给出q次查询,回答字符串第k个位置的字符。
Got MLE:
1. #include<bits/stdc++.h>
2. using namespace std;
3. typedef long long ll;
4. const int N=1e5+10;
5. int main()
6. {
7. int t;
8. cin>>t;
9. while (t--)
10. {
11. int n,c,q;
12. cin>>n>>c>>q;
13. string s;
14. cin>>s;
15. while (c--)
16. {
17. ll l,r;
18. cin>>l>>r;
19. s+=s.substr(l-1,r-l+1); //注意下标
20. }
21. while (q--)
22. {
23. ll k;
24. cin>>k;
25. cout<<s[k-1]<<endl;
26. }
27. }
28. return 0;
29. }
所有通过操作得到的字符都可以在原来的字符串中找到,每进行一次复制黏贴操作新字符串的元素可以追踪得到,记录这个关系,直到在原字符串中找到。
AC:
1. #include<bits/stdc++.h>
2. using namespace std;
3. typedef long long ll;
4. const int N=1e5+10;
5. ll slen[N],lb[N],diff[N];
6. int main()
7. {
8. int t;
9. cin>>t;
10. while (t--)
11. {
12. int n,c,q;
13. cin>>n>>c>>q;
14. string s;
15. cin>>s;
16. slen[0]=n,lb[0]=0;
17. for (int i=1;i<=c;i++)
18. {
19. ll l,r;
20. cin>>l>>r;
21. l--; //转化下标
22. r--;
23. lb[i]=slen[i-1]; //记录新添加的复制串起始位置
24. slen[i]=slen[i-1]+r-l+1; //记录新字符串长度
25. diff[i]=lb[i]-l;
26. }
27. while (q--)
28. {
29. ll k;
30. cin>>k;
31. k--;
32. for (int i=c;i>0;i--)
33. {
34. if (k<lb[i])
35. continue;
36. k-=diff[i];
37. }
38. cout<<s[k]<<endl;
39. }
40. }
41. return 0;
42. }
剩余题目可参考官方题解:Codeforces Round #807 (Div 2.) Editorial - Codeforces