日期:2023.10.3
学号:S09235
一:
总分数:
T1【
称心如意(satisfied)】:100
T2【AC万岁(acok)】:10
T3【解救达达(rescue)】:20
T4【整理文本(text)】:0
二、比赛过程
按时间顺序记录了一下
第一题是一道模拟题,出的也很水,一般来说模拟题这种简单的类型会出较难理解的题目,不过这个题只要按题目要求模拟,就可以拿全分,当时我也是很轻松的拿了全分。第二题也很简单,不过需要多想一点,就是c在a前面的情况,这样就可以推翻a的数量乘c的数量得到答案的结论。我就是没想到这一点,轻敌了,故只得了10分。第三题就开始上难度了,这个题用到了位运算:^>><<等,我看到数据量很大时,是想到了位运算,但是由于不会运用位运算解决,所以当时就像枚举一下,不过写着写着好像就超限了,我发现本来的枚举变成了暴力,就很无奈,后来就继续写下去,直达写完,超限后就去敲第四题。第四题用的是二分,我当时想的是贪心,但是后来改变思路想到了二分,但不敢写,因为这个题我怕二分不能拿分,直到最后,我写了个我自己都不知道是什么的算法,没得分。搞完这个就去搞第三题,改进了一下纯暴力,拿了20分。
三、比赛分析
T1【称心如意(satisfied)】
1、题目大意
就是要组成满足以下条件的序列:
1、序列的长度为N+1
2、假设序列第i位取值为j(j的范围为1到9),那么需要满足能整除 N%i==0,并且需要满足i能整除N/j,即i%(N/j)==0。
3、满足条件2的基础上, 的取值应该尽量小。
4、若条件2不能满足,那么第 位输出一个 -。
2、比赛中的思考
没啥好说的,按要求模拟即可。
3、解题思路
简单说下步骤:
(1)循环1~n+1,每次判断条件2。
(2)标记、输出。
(3)判断标记,输出-。
4、AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,Min=1005,flag=1;
scanf("%d",&n);
for(int i=0;i<=n;i++){
flag=1;
for(int j=1;j<=9;j++){
if(n%j==0&&i%(n/j)==0){
flag=0;
printf("%d",j);
break;
}
}
if(flag) printf("-");
}
return 0;
}
T2【AC万岁(acok)】
1、题目大意
就是给定一个字符串,删除几个元素,看能组成几个ac的字符串。
2、比赛中的思考
我当时想到的结论是a的数量乘c的数量,就是子串的数量,不过下面这个样例可以推翻:
aaacccccaaaa
错误代码(正确代码放下面了):
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
long long a=0,c=0;
for(int i=0;i<s.size();i++){
if(s[i]=='a') a++;
else c++;
}
printf("%lld",a*c);
return 0;
}
这个就不能说ca还满足,正确思路是:
统计a,后面有一个c,就累加a的个数,输出ans。
3、解题思路
(1)遍历字符串。
(2)判断如果当前是a,则计数器+1,判断等于c累加计数器。
(3)输出累加后ans的值。
4、AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int ans=0,a=0;
for(int i=0;i<s.size();i++){
if(s[i]=='a') a++;
if(s[i]=='c') ans+=a;
}
printf("%d",ans);
return 0;
}
T3【解救达达(rescue)】
1、题目大意
求区间【a,b】中数字二进制中包含几个0,0<2的情况伤害+1
2、比赛中的思考
当时想到位运算,然后不会写,就写了一个纯暴力,后来改进了一下,得了20分。
3、解题思路
4、AC代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a,b,L,R,aa,bb,x,y,ans;
int main(){
scanf("%lld%lld",&a,&b);
aa=a,bb=b;
while(aa){
L++;
aa>>=1;
}
while(bb){
R++;
bb>>=1;
}
while(L<=R){
x=((ll)1<<L)-1;
for(ll j=0;j<L-1;j++){
y=x^((ll)1<<j);
if(y>=a&&y<=b) ans++;
}
L++;
}
printf("%d",ans);
return 0;
}
T4【整理文本(text)】
1、题目大意
求的是当所有文本按照要求整理结束之后,得到每一行的行宽,使得其中最大的行宽最小,输出对应的行宽值。
2、比赛中的思考
当时想到了贪心,觉得不对,想到二分,觉得不对,甚至想到动规,更觉得不对,最后写了个K系列的代码,不对,0分。
3、解题思路
套用最小化答案模板代码,编写check()函数,具体如下:
宽度的取值一定是具有单调性的,可以进行二分查找,二分的范围为,每个单词的最大长度到一个很大的数字(可以装下全部单词),然后贪心验证每个枚举出来的宽度是否满足m行装下所有单词。
4、AC代码
//标准代码
#include<bits/stdc++.h>
#define N 220000
using namespace std;
long long a[N]={},m=0,mx=0,n=0;
bool calc(long long num);
int main(){
scanf("%lld%lld",&n,&m);
for(long long i=1;i<=n;i++){
scanf("%lld",&a[i]);
mx=max(mx,a[i]);
}
//输入,并找到单词长度最大值
long long l=mx-1,r=1e15+1;
//确定双端便边界,开始二分
while(l+1<r){
long long mid=(l+r)/2;
if(calc(mid)==false){
l=mid;
}else{
r=mid;
}
}
printf("%lld\n",r);
//输出答案r
return 0;
}
bool calc(long long num){
long long now=1,sum=-1;
//now为当前用到第几行,sum表示用到第now行的第几列 //初始值是为了把第一个单词前面的空格磨平
for(long long i=1;i<=n;i++){
if(sum+1+a[i]<=num){
sum=sum+1+a[i];
//当前行能在放就放
}else{
sum=a[i];
now++;
//放不了就另起一行
}
}
if(sum==-1){
now--;
}
//把初始值的误差过滤掉
if(now<=m){
return true;
}
//符合条件返回true
return false;
//否则返回false
}
//我的代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m,a[1000000],L,R;
bool check(ll mid){
ll sum=0,line=1;
for(int i=1;i<=n;i++){
if(sum+a[i]==mid) sum+=a[i];
else if(sum+a[i]+1<=mid) sum+=a[i]+1;
else{
sum=a[i]+1;
line++;
}
}
return line<=m;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
L=max(L,a[i]);
R+=a[i]+1;
}
while(L<R){
ll mid=L+R>>1;
if(check(mid)) R=mid;
else L=mid+1;
}
cout<<R;
return 0;
}