【CSP-J 2021】1.分糖果
此题考查的主要是数学归纳能力,与前几年套路一样,都是先水后难
整合题意:在[L,R]内找到一个x,使x%n最大
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,l,r,i,maxx=0,k;
scanf("%d %d %d",&n,&l,&r);
k=r/n*n-1;
if(k>=l)
printf("%d",k%n);
else if(r/n==1)
printf("%d",r%n);
else
{
for(i=k+1;i<=r;i++)
maxx=max(maxx,i%n);
printf("%d",maxx);
}
return 0;
}
【CSP-J 2021】2.插入排序
核心知识点:双向索引,对排序的理解,对时间复杂度的理解
用时间复杂度来分析题目,查询上限200000,n上限8000,常规做法每轮查询都排序时间复杂度200000* 8000* 13,肯定时间爆掉了,
再看修改数据5000,5000* 8000复杂度是跑不掉的,那外面也套不了什么优化了,所以这题时间复杂度就是5000* 8000+(200000-5000),命令2是O(1)
这题就是用rank和sa数组
具体做法
1.读入数据,排序,生成rank和sa数组
2.碰到2命令直接输出,碰到1命令,修改arr中的值,然后判断是向前做一趟排序,还是向后做一趟排序,同时改变sa和rank的值,change函数实现
注:sa[i]=j表示排序后的第i个元素在原数组的第j个,rk[i]=j表示原数组的第i个元素在排序后的第j个
#include<bits/stdc++.h>
using namespace std;
typedef struct node
{
int id,v;
bool operator < (const struct node & t)const
{
if(v==t.v)
return id<t.id;
return v<t.v;
}
}NODE,*PNODE;
NODE arr[8005];
int sa[8005],rk[8005],n;
void change(int id,int v)
{
int k=rk[id];
bool s;
if(v==arr[k].v)//不变就直接返回
return;
s=arr[k].v>v;//s用来判断是比原值大了还是小了
arr[k].v=v;
if(s)
{
for(;k>=1&&arr[k]<arr[k-1];k--)
{
swap(arr[k],arr[k-1]);
swap(rk[sa[k]],rk[sa[k-1]]);
swap(sa[k],sa[k-1]);
}
}
else
{
for(;k<n&&arr[k+1]<arr[k];k++)
{
swap(arr[k],arr[k+1]);
swap(rk[sa[k]],rk[sa[k+1]]);
swap(sa[k],sa[k+1]);
}
}
}
int main()
{
int i,q,a,b,tt;
scanf("%d %d",&n,&q);
for(i=1;i<=n;i++)
{
scanf("%d",&arr[i].v);
arr[i].id=i;
}
sort(arr+1,arr+1+n);
for(i=1;i<=n;i++)
{
sa[i]=arr[i].id;
rk[arr[i].id]=i;
}
while(q--)
{
scanf("%d",&tt);
if(tt==2)
{
scanf("%d",&a);
printf("%d\n",rk[a]);
}
else if(tt==1)
{
scanf("%d %d",&a,&b);
change(a,b);
}
}
return 0;
}
【CSP-J 2021】3.网络连接
核心知识点:字符串处理、map运用、string、基础代码能力
这题对学生综合素养要求比较高
用时间复杂度来分析题目,看数据规模这题就不是用来考时间复杂度的,就是考查字符串功底和数据结构的水平
具体做法如下:
1.把输入数据分两个字符串接收 cmd和adr
2.主要处理adr
(1)因为你不知道它数字符号怎么组,顺着验非常烦,不如把数字都取出来,不足或超过4个超范围都直接非法,
然后把四个数字拼成合法地址,回验adr合法性,这样还能带走前导0的问题
(2)利用map做到下标对位的功能,map中string直接对位地址,int可以记录合法编号
注:自己分析这题还是有些好处的,这里就提供一个框架
#include<bits/stdc++.h>
using namespace std;
char cmd[10],adr[30],tmp[30];
int arr[30],a_l=0;
map<string,int> mp;
int cnt;
void get_num()//字符串基本技巧,把数字读出来放入arr数组
{
}
int check()
{
/*
....
*/
if()//判断是否合法
{
memset(tmp,0,sizeof(tmp));
sprintf(tmp,"%d.%d.%d.%d:%d",arr[0],arr[1],arr[2],arr[3],arr[4]);
if(strcmp(tmp,adr)==0)//技巧,读出合法数量的数字,拼出合法字符串和原地址进行对比
return 1;
else
return 0;
}
else
{
return 0;
}
}
int main()
{
int n;
string str;
scanf("%d",&n);
while(n--)
{
scanf("%s %s",cmd,adr);
cnt++;
if()//验地址是否合法
{
printf("ERR\n");
continue;
}
str=adr;
if(cmd[0]=='S')
{
//利用map来检验server合法性
}
else if(cmd[0]=='C')
{
//利用map来检验client合法性
}
}
return 0;
}
【CSP-J 2021】4.小熊的果篮
核心知识点:队列,分块
用时间复杂度来分析题目,最大数据范围200000,看看题目也没啥好二分的,基本上时间开销就是线性访问每个数据,再加上外循环处理每轮的连续快,所以一定是用链表做删除
考试考的很基础,但是对基础代码的要求比较高
构造一个block的结构体,上来类似去重处理,每个块记录开始位置lt和结束位置rt,然后块号对齐下标
构造pre和nxt链表,使得处理过的点删除的时候达到O(1)时间复杂度
自己如果写循环控制会写炸,利用STL的deque大大简化处理
具体做法如下:
1.把数据读入arr,然后处理出block数组,初始化链表pre、nxt,把块号放入q[0](利用两个deque对倒)
2.从一个队列中把块一个个读出来处理,然后处理完的块放入新队列,为下一轮做准备
(1)从队列中把每个块的第一个元素输出,从链表中删除
(2)删完如果块空了,就不用处理了,如果块长度大于1,看一下是否和新队列中最后一个块的值一样,一样的就合并这两个块,不一样就把处理完的块加入新队列
3.交换队列重复上面处理方法
#include<bits/stdc++.h>
using namespace std;
typedef struct block
{
int lt,rt;
}BLOCK,*PBLOCK;
BLOCK b[200005];
int b_l;
int pre[200005],nxt[200005];
int arr[200005];
deque<int> q[2];
int k=0;
int main()
{
int n,i,j,flag=0,pos,tmp;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&arr[i]);
pre[i]=i-1;
nxt[i]=i+1;
}
b[++b_l].lt=1;
for(i=2;i<=n;i++)
{
if(arr[i]!=arr[i-1])
{
b[b_l++].rt=i-1;
b[b_l].lt=i;
}
if(i==n)
b[b_l].rt=n;
}
for(i=1;i<=b_l;i++)
q[k].push_back(i);
while(!q[k].empty())
{
flag=0;
while(!q[k].empty())
{
tmp=q[k].front();
q[k].pop_front();
if(!flag)
{
printf("%d",b[tmp].lt);
flag=1;
}
else
printf(" %d",b[tmp].lt);
pos=b[tmp].lt;
pre[nxt[pos]]=pre[pos];
nxt[pre[pos]]=nxt[pos];
if(b[tmp].lt==b[tmp].rt)
continue;
b[tmp].lt=nxt[pos];
if(!q[k^1].empty()&&arr[b[q[k^1].back()].rt]==arr[b[tmp].lt])
b[q[k^1].back()].rt=b[tmp].rt;
else
q[k^1].push_back(tmp);
}
printf("\n");
k^=1;
}
return 0;
}