Codeforces Round #748 (Div. 3)解题报告
总结
场上做了ABCD1E,但其实题目都挺简单的,还是自己太菜了,唉。
%%%xmz神仙,AK了。
A. Elections
求使一个数变成三个数中最大的数最小加几。
做法很多,但是注意不能直接求max然后减,要判断。
B. Make it Divisible by 25
因为25是100的因数,所以只要保证剩下的数最后两位是25的倍数(也就是00/25/50/75)即可。
分类讨论一下。
C. Save More Mice
贪心:一定先让靠近n的老鼠移动。
把老鼠坐标从大到小排序,用two pointers随便模拟一下即可。
D1. All are Same
首先显然的是最后一定都变成了最小的a。
答案即为各个a与最小的a的差的gcd(要特判0)。
如果数全部相同,则为正无穷(-1)。
D2. Half of Same
不确定最终都变成了哪个数字,于是我们枚举。
然后其他a与这个枚举的a作差,得到n个差。
问题就变成了选n/2-1个差,使其gcd最大。
于是可以 \(O(\sqrt a)\) 求出每个差的因数,开个桶记下来。
然后从大到小遍历一遍桶,找到第一个符合条件的因数即为答案。
当差为0的数量已经满足条件了,则答案为-1。
E. Gardener and Tree
给定一棵树,每次操作删掉所有的叶子节点,问经过k次操作后还剩下的节点数。
一开始写了个树形dp,结果wa了三发后发现写假了。rating--
正解就直接模拟,记录一下每个节点的度数,用类似拓扑排序的方式遍历一遍即可。
-------比赛分割线----------
剩下的就是补的题了。
F.Red-Black Number
读错题了。
正确题意:
给一个序列,将其每一位染成红色或者黑色。
要求红色组成的数字是 A 的倍数,黑色组成的数字是 B 的倍数。
输出满足上述要求时红黑两色数量差最小的染色方案。
思路:
首先可以发现,过程中我们不需要知道当前数字是多少,只要知道当前两个数字的位数和除以 \(A/B\) 余数分别是多少。
所以我们设 \(dp[i][j][a][b]\) 表示前 \(i\) 位染 \(j\) 个黑色,红/黑色组成的数字模 \(A/B\) 时的余数分别为 \(a/b\) 的方案是否存在。
然后再用 \(black[i][j][a][b]\) 和 \(red[i][j][a][b]\) 记录一下路径(记录从哪个状态转移过来),最后倒序找路径、正序输出路径即可。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
string s;
int T,n,A,B,dp[45][45][45][45],black[45][45][45][45],red[45][45][45][45];
inline void print_ans(int ans){//倒序找路径并正序输出
int anss[50],a=0,b=0;
for(int i=n;i>=1;i--){
if(black[i][ans][a][b]!=-1){
anss[i]=0;
b=black[i][ans][a][b];
ans--;
}else{
anss[i]=1;
a=red[i][ans][a][b];
}
}
for(int i=1;i<=n;i++){
putchar(anss[i]?'R':'B');
}
puts("");
}
int main(){
cin>>T;
while(T--){
memset(dp,0,sizeof(dp));
memset(black,-1,sizeof(black));
memset(red,-1,sizeof(red));
cin>>n>>A>>B;
cin>>s;
dp[0][0][0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
for(int a=0;a<A;a++){
for(int b=0;b<B;b++){
if(dp[i-1][j][a][b]){//转移并记录路径
dp[i][j+1][a][(b*10+s[i-1]-'0')%B]=1;
black[i][j+1][a][(b*10+s[i-1]-'0')%B]=b;
dp[i][j][(a*10+s[i-1]-'0')%A][b]=1;
red[i][j][(a*10+s[i-1]-'0')%A][b]=a;
}
}
}
}
}
int ans=50;
for(int i=1;i<n;i++){
if(dp[n][i][0][0]){
if(abs(n-i-i)<abs(n-ans-ans)) ans=i;
}
}
if(ans==50) cout<<-1<<endl;
else print_ans(ans);
}
return 0;
}
G.Changing Brackets
没想到是个萌萌题。
因为左右括号可以随意转化,中括号可以转为小括号。
所以只需要找到区间内中括号的位置。答案实际上就是区间中括号在奇数位置和偶数位置数量的差的绝对值。
如果要构造方案也很简单,只需要用一个栈像括号匹配一样匹配即可。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<bitset>
using namespace std;
const int maxn=1e6+5;
int T,len,n,cnt1[maxn],cnt2[maxn];
string s;
int main(){
ios::sync_with_stdio(false);
cin>>T;
while(T--){
cin>>s>>n;
int len=s.length();
s=' '+s;
for(int i=1;i<=len;i++){
cnt1[i]=cnt1[i-1];cnt2[i]=cnt2[i-1];
if(s[i]=='['||s[i]==']') i&1?cnt1[i]++:cnt2[i]++;
}
for(int i=1;i<=n;i++){
int l,r;
cin>>l>>r;
cout<<abs((cnt1[r]-cnt1[l-1])-(cnt2[r]-cnt2[l-1]))<<endl;
}
}
return 0;
}