CSP-X复赛模拟二————韩柄睿补题

日期: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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值