> 文档中心 > Educational Codeforces Round 121 (Rated for Div. 2) 简训

Educational Codeforces Round 121 (Rated for Div. 2) 简训

Educational Codeforces Round 121 (Rated for Div. 2)简训

  • 导语
  • 涉及的知识点
  • 题目
      • A Equidistant Letters
      • B Minor Reduction
      • C Monsters And Spells
      • D Martial Arts Tournament
  • 参考文献

导语

日常训练

涉及的知识点

二分,思维,贪心

链接: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 #define int long longusing 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 #define int long longusing 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 hiki k i k_i ki都不同,每一秒造成的伤害取决于上一秒以及自己的选择,如果上一秒没有蓄力,那么伤害/蓄力值默认为1,否则伤害/蓄力值为上一秒的值+1或者是1,每一点蓄力/伤害的花费都是对应值,每个怪物只能被攻击一次,求出能够消灭所有怪物的最小花费

思路:见参考文献和代码

代码

#include #define int long longconst 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 #define int long longconst 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;}

参考文献

  1. Martial Arts Tournament
  2. Educational Codeforces Round 121 1626C Monsters And Spells