CSP-X复赛模拟五补题报告
时间:2023年10月15日
S12004 张伯钧
一、分数:
总分:60’
T1【项链】:0’
T2【扫地机器人】:0’
T3【二进制魔法】:0’
T4【寄居蟹】:60’
二、比赛过程:
第一题:
考试时不知道咋想的,认为数据范围超出了数组的范围,所以没用数组,要不然就可能全对了。【ㄟ( ▔, ▔ )ㄏ】
第二题:
稍微找到了点规律,当时以为这题稳了,后来发现还是思路上有问题。
第三题:
当时想着要骗点分,但我也不知道为啥没骗到。
第四题:
这道题我压根儿没抱什么希望,瞎写了个不确定代码,没想到拿了60分!!!【w(゚Д゚)w】
三、比赛分析:
第一题:
项链
时间限制:1秒 内存限制:128M
题目描述
小可现在手里有好多亮闪闪的小球,于是小可打算选择一些小球串成项链。但是,小可还不会怎么把项链首尾相接成环,所以小可只是打算暂时将这些小球串成一条链。
每种小球有一个愉悦值,但是如果一种小球连续出现D个,就会产生审美疲劳。每种小球的数量都可以认为是无限的。
请问,用m个小球串成一条链,总的愉悦值最大是多少?
注意:这里的链不成环,首位不相接,第一个小球和最后一个小球不相邻。
输入描述
第一行三个整数n,m,D,分别代表小球的种类数,需要用到的小球数,D的含义如题。
第二行n个整数a1,a2,⋯,an(1≤ai≤109) ,代表第i种小球的愉悦值是多少。
输出描述
输出一个正整数,代表总的愉悦值最大是多少。
样例输入
3 5 2
3 4 5
样例输出
23
数据范围
对于20%的数据,m≤1000
对于60%的数据,n≤10^5
对于100%的数据,1≤n≤10^6,1≤m,D≤10^9
思路:
用数组,先sort一下,然后找出最大值和第二大值,最后稍微计算一下输出就行了。
计算的思路大概是这样的:要想让愉悦值最大,而且一个珠子不能重复D次,先串D-1个愉悦值最大的珠子,然后再串一个愉悦值第二大的珠子,重复这个步骤,直到串完m个珠子。
代码实现:
#include<iomanip>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#define ll long long
using namespace std;
ll a[1000005];
int main(){
ll n,m,d,max,max2,ans=0;
cin>>n>>m>>d;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);//排序
max=a[n];//最大值
max2=a[n-1];//第二大值
ans=(m/d)*(max*(d-1)+max2)+(m%d)*max;//公式
cout<<ans;
return 0;
}
第二题:
扫地机器人
时间限制:1秒 内存限制:128M
题目描述
数轴上有一个扫地机器人,每次会到某个正整数的位置上打扫垃圾,然后回到坐标为00的位置。
每次出现垃圾,机器人必须前往这个坐标清理垃圾,然后回到坐标00。机器人不能清理沿途的其他垃圾。并且,如果一个坐标会出现x次垃圾,那么机器人必定会前往这个坐标x次。
小可统计了一些坐标会出现垃圾的次数。小可可以将这些出现次数分配到一些坐标上。请问,机器人需要经过的距离最小是多少?
垃圾出现的坐标可以是正整数或者负整数,垃圾不会在坐标0出现。但是我们在保证总距离最小的前提下,尽量少往负坐标走。
输入描述
第一行一个数字n
第二行n个整数a1,a2,⋯,an,表示某个坐标会出现ai次垃圾。
输出描述
第一行一个整数,代表机器需要经过的总距离的最小值。
第二行n个整数。第i个数字代表了出现了ai次垃圾的坐标。
样例输入
5
1 4 5 2 0
样例输出
30
-2 -1 1 2 3
数据范围
对于50%的数据,n≤100,ai≤100
对于100%的数据,n≤2×10^5,ai≤10^6,ai两两不同,保证答案唯一。
思路:
用结构体(为了后面按顺序输出),排序,遍历分情况判断,每次一个正坐标,一个负坐标交替着存。
代码实现:
#include<iomanip>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#define ll long long
using namespace std;
struct node{
int id;
int data;
}a[200005];
bool cmp(node a,node b){
return a.data>b.data;
}
bool cmp1(node a,node b){
return a.id<b.id;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i].data);
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
ll sum=0,cnt=0;
for(int i=1;i<=n;i++){
if(i%2==1){//正数
++cnt;
sum+=2*cnt*a[i].data;//来回两趟
a[i].data=cnt;
}
else{//负数
sum+=2*cnt*a[i].data;//来回两趟
a[i].data=-cnt;
}
}
cout<<sum<<endl;
sort(a+1,a+n+1,cmp1);
for(int i=1;i<=n;i++){
printf("%d ",a[i].data);
}
return 0;
}
第三题:
二进制魔法
时间限制:1秒 内存限制:128M
题目描述
小可现在有两个二进制串S和T,现在小可施展了魔法,想把S变成T。对S可以做两种操作:
1、交换Si和Sj,消耗的魔力值是∣i−j∣
2、对Si取反,消耗的魔力值是11
请问将S变成T,小可消耗的魔力值最小是多少?
输入描述
第一行一个整数n,代表这两个二进制串的长度。
第二行和第三行分别一个二进制串,表示S和T。
输出描述
一个整数,代表小可消耗的魔力值的最小值是多少。
样例输入1
2
1
10
样例输出1
1
样例输入2
3
011
100
样例输出2
2
数据范围
对于20%的数据,n≤20
对于另外20%的数据,S,T之间最多有两个位置不同。
对于60%的数据,n≤10^5
对于100%的数据,1≤n≤10^6
思路:
遍历,判断不一样的地方是交换合适,还是取反合适。
代码实现:
#include<iomanip>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#define ll long long
using namespace std;
int main(){
int n;
string s,t;
cin>>n;
cin>>s>>t;
int cnt=0;
for(int i=0;i<n;i++){
//相邻不一样 且和t对应不相等 交换(1次)比修改(2次) 效率高
if(s[i]!=t[i]&&s[i+1]!=t[i+1]&&s[i]!=s[i+1]){
cnt++;
swap(s[i],s[i+1]);
}
else if(s[i]!=t[i]){
cnt++;
if(s[i]=='0') s[i]='1';//可加可不加
else s[i]='0';//可加可不加
}
}
cout<<cnt;
return 0;
}
第四题:
寄居蟹
时间限制:1秒 内存限制:128M
题目描述
寄居蟹喜欢居住在螺壳中。小可养了许多寄居蟹,于是小可打算给每个寄居蟹准备一个小的螺壳和一个大的螺壳。
小可认为,一个大小为x的螺壳,必须和一个大小为k×x的螺壳在一起,才能给一个寄居蟹使用。
现在给定了n个螺壳的大小,请问按照上述规则匹配之后,最少剩下多少未匹配的螺壳?
输入描述
多组输入。第一行一个正整数t表示数据组数。
每组数据第一行两个正整数表示 n 和 k。
第二行n个正整数表示每个螺壳的大小。
输出描述
每组数据输出一个整数,代表最少剩余多少个螺壳未匹配。
样例输入
4
1 7
1
2 2
1 2
3 2
1 2 4
4 3
1 3 9 27
样例输出
1
0
1
0
数据范围
对于20%的数据,n=1
对于60%的数据,∑n≤10^5
对于100%的数据,1≤k≤10^9,∑n≤5×10^5。每个螺壳的大小不超过109。
思路:
非常复杂,要用map,我先讲一下我的做法(我这道题是蒙的,我也不知道为什么得了60):
遍历数组,判断a[i]是否等于-1,如果不是,从i+1开始遍历。如果a[i]*k==a[j]&&a[j]!=-1那么把他们都变成-1,然后break。
最后输出a[i]为-1的个数。
代码实现(60):
#include<iomanip>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#define ll long long
using namespace std;
int a[500005];
int main(){
freopen("hermit.in","r",stdin);
freopen("hermit.out","w",stdout);
int t,n,k;
cin>>t;
while(t--){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(a[i]!=-1){
for(int j=i+1;j<=n;j++){
if(a[i]*k==a[j]&&a[j]!=-1){
a[i]=-1;
a[j]=-1;
break;
}
}
}
}
int c=0;
for(int i=1;i<=n;i++){
if(a[i]!=-1){
c++;
}
}
cout<<c<<endl;
}
fclose(stdin);
fclose(stdout);
return 0;
}
代码实现(100):
#include<iomanip>
#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#define ll long long
using namespace std;
const int N=5e5+10;
ll a[N];
map<ll,int>mp;
int main(){
ll t,n,k;
scanf("%lld",&t);//cin>>t;
while(t--){
scanf("%lld%lld",&n,&k);//cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
mp[a[i]]++;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(k==1){
if(mp[a[i]]>1){
mp[a[i]]-=2;
}
}
else if(mp[a[i]]>=1&&mp[a[i]*k]>=1){//mp.count(a[i]*k)
mp[a[i]*k]--;
mp[a[i]]--;
}
}
ll cnt=0;
map<ll,int>::iterator it;
for(it=mp.begin();it!=mp.end();it++){
if(it->second>=1){
cnt+=it->second;
}
}
printf("%lld\n",cnt);
mp.clear();
}
return 0;
}
总结:
多找规律(前两题)多想想,最后一题其实很容易拿分。