Educational Codeforces Round 121 (Rated for Div. 2)简训
导语
日常训练
涉及的知识点
二分,思维,贪心
链接:Educational Codeforces Round 121 (Rated for Div. 2)
题目
A Equidistant Letters
题目大意:给定一个只有小写字母的字符串s,每个字母出现不超过2次,现在需要重新排列字符串使得每两个相同字母间的距离相等,例如abcdcba,a和a间距6,b和b间距4,c和c间距2,改成ababcdc后,间距都为2
思路:直接把一样的堆在一起就行了
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n,alpha[30];
signed main() {
scanf("%lld",&t);
while(t--) {
string s;
cin >>s;
memset(alpha,0,sizeof(alpha));
int len=s.size();
for(int i=0; i<len; i++)
alpha[s[i]-'a']++;
for(int i=0; i<30; i++)
while(alpha[i]--)printf("%c",i+'a');
cout <<endl;
}
return 0;
}
B Minor Reduction
题目大意:给出一个最多200000位的十进制非负整数x,没有前导0,定义一种操作:对x选取相邻的两位进行十进制相加然后替换原来的两位,例如10057,选取末两位得到10012,现在只能进行一次操作,求出操作之后能够得到的最大的数字是多少
思路:从低位向高位遍历,如果存在两位加起来超过10,则直接操作即可,否则操作最高位
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n;
char s[200005];
signed main() {
scanf("%lld",&t);
while(t--) {
scanf("%s",s);
int len=strlen(s);
bool flag=0;
for(int i=len-1; i>0; i--) {
int x=s[i]-'0',y=s[i-1]-'0';
if(x+y>=10) {
x=x+y;
s[i]=x%10+'0';
s[i-1]=x/10+'0';
flag=1;
break;
}
}
if(flag) {
printf("%s\n",s);
continue;
}
s[1]=s[0]+s[1]-'0';
printf("%s\n",s+1);
}
return 0;
}
C Monsters And Spells
题目大意:n个怪兽,第i个怪兽在开始之后只连续出现 k i k_i ki秒,有 h i h_i hi血,保证 h i ≤ k i h_i\le k_i hi≤ki且 k i k_i ki都不同,每一秒造成的伤害取决于上一秒以及自己的选择,如果上一秒没有蓄力,那么伤害/蓄力值默认为1,否则伤害/蓄力值为上一秒的值+1或者是1,每一点蓄力/伤害的花费都是对应值,每个怪物只能被攻击一次,求出能够消灭所有怪物的最小花费
思路:见参考文献和代码
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
using namespace std;
const int maxn=2e5+5;
int t,n,k[maxn],h[maxn];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n;
for(int i=1; i<=n; i++)cin >>k[i];
for(int i=1; i<=n; i++)cin >>h[i];
int kn=k[n],hn=h[n],sum=0;
for(int i=n-1; i>=1; i--) {
if(kn-hn<=k[i]-h[i])continue;
//如果使n怪物消灭的起始点比i的起始点小,代表不需要更改
if(kn-hn<k[i])
hn+=kn-hn-(k[i]-h[i]);//否则就要增加伤害,起始点还需要前移
else {
sum+=(1+hn)*hn/2;//代表已经超出hn能够管理的范围了
hn=h[i],kn=k[i];//开始一个新的区域
}
}
sum+=(1+hn)*hn/2;
cout <<sum<<endl;
}
return 0;
}
D Martial Arts Tournament
题目大意:一场比赛分成三个等级:轻量级,中量级,重量级,每个等级内部进行锦标赛赛制,现在知道每个人的值,需要将所有人根据值划分成三个等级,并且每个等级必须有人,最后要让每个等级的人数都是2的整数次幂(锦标赛赛制),现在要决定两个边界来分出三个等级,不够的人数需要增加,现在需要使得增加的人数最少,求出加的最少人数
思路:由于给定的范围不大,可以直接暴力尝试区间,预处理各个值的人数以及人数前缀和,在尝试区间的时候直接二分即可,时间复杂度为 O ( n + ( log 2 n ) 3 ) O(n+(\log_2n)^3) O(n+(log2n)3)
代码
#include <bits/stdc++.h>
#define int long long
const int inf=0x3f3f3f3f;
using namespace std;
const int maxn=2e5+5;
int t,n,mp[maxn],pre[maxn];
int place(int l,int r) {
int ans=0;
int posl=upper_bound(pre+1,pre+1+n,l)-pre;//找到首个大于边界长度的位置
posl--;//减1将posl及之前的全部划分给L
ans+=l-pre[posl];
int posr=upper_bound(pre+1,pre+1+n,r+pre[posl])-pre;
posr--;
ans+=r-pre[posr]+pre[posl];
int p=1;
while(pre[n]-pre[posr]>p)p<<=1;//为剩余长度填人
ans=ans+p-pre[n]+pre[posr];
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >>t;
while(t--) {
cin >>n;
int res=inf,x;
memset(mp,0,sizeof(mp));
for(int i=1; i<=n; i++)cin >>x,mp[x]++;//开桶记录
for(int i=1; i<=n; i++)pre[i]=pre[i-1]+mp[i];//记录前缀和,出现次数
for(int i=1; i<=n; i<<=1)
for(int j=1; j<=n; j<<=1)//尝试不同边界
res=min(res,place(i,j));
cout <<res<<endl;
}
return 0;
}