CSP-J复赛模拟补题报告
2023.10.2星期one
目录
1.比赛分数
共四题,满分400,比赛中拿到220分
T1(down):90
T2(group):100
T3(seize):20
T4(barrier):10
2.比赛过程
T1想到了合数可以被分为一个质数乘其他自然数,所以只需要判断这个数是不是合数就可以做。
T2第一眼看出来不是一个考验算法知识的,就造了几组数据尝试寻找规律(深知自己遇到算法就蒟蒻)大概过了10分钟找到思路打码也没遇到什么问题。
T3一开始以为是排序然后看变化,但却发现是更改数据不是交换数据,最后发现时间不够了就直接暴力打的,结果赛后讲评一看才发现这道题好难♂。
T4本来以为不可能做出来就放弃了,先把样例1用更完整的情况骗了下分(就是谈论小可以及达达利亚可以直接过去的情况)为此看了一遍题感觉可以做,所以就浅浅打了打(甚至“==”打成了“=”)结果后面发现就是一个比较简单但是又很多种特殊情况需要讨论的贪心,但是还剩最后17分钟,等到收卷还剩下1种情况没有讨论。把freopen补上就交了最后发现题目理解有问题,他们是从0开始走不是第一个数,以及我中间的测试数据都没有删干净还是比较后悔的,但凡能多看看就能从10分到60分(后面将数组从0开始记并且多打上4行删掉2行就能60)
3.题解报告
(1)down数字降级
情况:赛中90,已补题
题意:输入一个数,将数除以它的任意一个因数,求除多少次能将其分解为一个质数
题解:判断质数,是0否1(唯一分解定理)
AC奉上:
#include<iostream>
using namespace std;
int main(){
// freopen("down.in","r",stdin);
// freopen("down.out","w",stdout);
long long n;
cin>>n;
for(long long/*十年OI一场空,不开LL见祖宗*/ i=2;i*i<=n;i++){//不优化会爆,优化为1/2也会爆
if(n%i==0&&i!=n){
cout<<1;
return 0;
}
}
cout<<0;
// fclose(stdin);
// fclose(stdout);
return 0;
}
(2)分组group
情况:赛中AC,赛后补了补锌Ⅳ🦌
题意:把n个数分成多组,求每组中最小的自然数加起来最大的数值
题解:桶标记完后while循环找0找到零就把前面所有的串结算一下外面再套个while没有0时结束并累加里面while的值。
介个是AC代码:
#include<iostream>
using namespace std;
int main(){
// freopen("group.in","r",stdin);
// freopen("group.out","w",stdout);
int n,a[1005]={0},x,sum=0,min=100005,ret=0;
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
a[x]++;
}
while(a[0]!=0){
sum=0;
min=100005;
while(a[sum]!=0){
if(a[sum]<=min){
min=a[sum];
}
sum++;
}
ret+=sum*min;
for(int i=0;i<=sum;i++){
a[i]=a[i]-min;
}
}
cout<<ret;
// fclose(stdin);
// fclose(stdout);
return 0;
}
(3)抢夺地盘
情况:赛中暴力20,已补题
题意:有n个城镇代表的钱,实现前p个为不下降的序列,后面p到n为不上升的序列,求实际与序列差多少个。
题解:分别求两个不同方向的最长上升子序列(1~p-1;p+1~n),比较长度上的不同并输出。dp时复为n²,通过2分优化为nlog(n)
想了好久QWQ:
#include<iostream>
using namespace std;
int main(){
int a[100005]={0},lis[100005]={0},sum=1,ans=0,n,p;
cin>>n>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
lis[1]=a[1];
for(int i=2;i<p;i++){
if(a[i]>=lis[sum]){
lis[sum++]=a[i];
}
else{
int L=1,r=sum,mid=sum>>1;
while(L!=r){
if(a[i]<lis[mid]){
r=mid;
}
else L=mid+1;
mid=(L+r)>>1;
}
lis[L]=a[i];
}
}
if(a[p]>=lis[sum]){
sum++;
}
else{
a[p]=1145141919810;
}
ans+=p-sum;//上半
sum=1;
lis[1]=a[p];
for(int i=p+1;i<=n;i++){
if(a[i]<=lis[sum]){
lis[sum++]=a[i];
}
else{
int L=1,r=sum,mid=sum>>1;
while(L!=r){
if(a[i]>lis[mid]){
r=mid;
}
else{
L=mid+1;
}
mid=(L+r)>>1;
}
lis[L]=a[i];
}
}
ans+=(n-p+1)-sum;
cout<<ans<<endl;
return 0;
}
(4)闯关barrier
情况:赛时10分,未补题
题意:两个人可以使用一个使跨度变大k-m的神器,但是因为只有一个所以需要来回使用,传的距离不超过q。问需要传多少次。
题解:大模拟,将几种特殊情况列出来解决。需要贪心思路。
60分代码(赛后)
#include<iostream>
using namespace std;
int main(){
// freopen("barrier.in","r",stdin);
// freopen("barrier.out","w",stdout);
int n,m,k,p,a[1005]={0},b[1005]={0},sum1=0,sum2=0,flag1=0,flag2=0,bang=0,ans=0;
cin>>n>>m>>k>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
if(i!=1){
if(a[i]-a[i-1]>sum1){
sum1=a[i]-a[i-1];
}
}
}
for(int i=1;i<=n;i++){
cin>>b[i];
if(i!=1){
if(b[i]-b[i-1]>sum2){
sum2=b[i]-b[i-1];
}
}
}
if(sum1<=m||sum2<=m){
if(sum2<=m){
cout<<0;
return 0;
}
if(sum1<=m){
cout<<1;
return 0;
}
}
while(flag1!=n||flag2!=n){
if((flag1==n&&bang==1)||(flag2==n&&bang==0)){
cout<<ans;
return 0;
}
if(bang==0){
while(a[flag1+1]-b[flag2]<=p&&flag1!=n){
flag1++;
}
}
else{
while(b[flag2+1]-a[flag1]<=p&&flag2!=n){
flag2++;
}
}
ans++;
if(bang==0){
bang=1;
}
else{
bang=0;
}
}
cout<<ans;
// fclose(stdin);
// fclose(stdout);
return 0;
}
四.赛后总结
第一题出现小错误没有AC,时间安排不当导致没有做完,比较可惜的。。。