文章目录
比赛过后总有些意难平,所以写一篇自我题解来舒缓一下。
首先是CSP2021游记
10月23日 7:55
跟同学进入杭师大,参加普及组比赛。
8:30
拿到题目,总体扫了一遍,第一反应,我靠,这么水的题目。
12:00
出考场,由于第一次参加比赛,发挥失误,内心忐忑。
1:45
短暂休整了一段时间再次进入考场参加提高组比赛
2:30
正式开始,旁边一堆敲代码的声音,整场考试都没停下来过,看到第一题想了两个半小时,想出了一个伪算法,拿不了满分害怕了,匆忙看剩下三题,第二题区间DP一看情况这么多,人吓没了,输出样例,开始暴力打第三题,一看能骗28分高兴坏了,最后一题搁这阅读理解没看懂。
6:30
出考场,讨论一下思路,距离A题差一点点,太可惜了。
10月30日 8:25
分数出了,这天刚好是我生日,卡了半天的官网总算进去了,普及247,提高83。对于这次比赛的发挥我不太满意,可是这么差的发挥有这么点分我满意了[滑稽]。
正式题解:
先发一下题目
S组题面
https://new.qq.com/omn/20211024/20211024A01S2X00.html
J组题面
https://www.sohu.com/a/496778772_120132276?spm=smpc.content.content.2.1635687118672arFuYig
J组题解
第一题 分糖果(candy)
题意:
在 L − R L-R L−R 颗糖果中选取 K K K 颗糖果,分给 n n n 个小朋友,要求 K m o d n K mod\ n Kmod n 尽可能大。
题解:
说实话真没想到第一题这么水,简直就是签到题(口误) 就是签到题。
听说CF还出了一道一模一样的题[笑哭]。
根据题意,尽可能选较大的就行了,先判断最大值存不存在,若存在,直接输出,否则输出
R
m
o
d
n
R\ mod\ n
R mod n 即可。
参考代码
//本人考场代码,未修
#include<bits/stdc++.h>
#define ll long long
#define FOR(i,n,m) for(int i=n;i<=m;i++)
using namespace std;
void read(int &x)
{
int ret=0;
char c=getchar(),last=' ';
while(!isdigit(c))
last=c,c=getchar();
while(isdigit(c))
ret=ret*10+c-'0',c=getchar();
x=last=='-'?-ret:ret;
}
int n,l,r;
int main()
{
freopen("candy.in","r",stdin);
freopen("candy.out","w",stdout);
read(n);
read(l);
read(r);
int x=l%n;
if(x==n-1)
{
printf("%d\n",x);
return 0;
}
x=(n-1)-x;
if(l+x<=r)
{
printf("%d\n",n-1);
return 0;
}
printf("%d\n",r%n);
return 0;
}
第二题 插入排序(sort)
题意:
给你一个长度为 n n n 的数列,有 Q Q Q 次询问和修改,
- 修改:改变某一个位置的数字(影响后续操作)
- 询问:对当前数列进行插入排序,求原来某一位置上的数经排序后在哪一个位置(不影响后续操作)
插入排序代码,详见题目,(稳定,即相同大小比较顺序不变)。
题解:
本题给出的
Q
≤
2
×
1
0
5
,
n
≤
8000
Q \leq 2\times 10^5 ,n \leq 8000
Q≤2×105,n≤8000 ,修改次数
≤
5000
\leq 5000
≤5000,对于每次查询都排序一次复杂度明显超标,能喜获52分。所以我们再进行思考,如果对于每次修改进行一次排序那就容易多了,我们脑袋一拍,发现对于每次修改,若之前是排序完整的数列,那么我们只需要将修改过的数往左右更新一下就好,开一个数组记录一下位置的改变输出答案就够了就可以了。
记得开始进行排序使得数列变得有序。so,水题+1;
参考代码:
#include<bits/stdc++.h>
using namespace std;
//By Spy_Savior
template<class T>inline void read(T&x){
x=0;
char c=getchar(),last=' ';
while(!isdigit(c))last=c,c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(last=='-')x=-x;
}
const int MAXN=8e3+5;
int n;
int a[MAXN];
struct node{
int id,val;
}b[MAXN],tmp[MAXN];
int ans[MAXN];
void merge_sort(int l,int r){
if(l>=r)return;
int mid=l+r>>1,i=l,j=mid+1,k=l;
merge_sort(l,mid),merge_sort(mid+1,r);
while(i<=mid&&j<=r){
if(b[i].val<=b[j].val)tmp[k++]=b[i++];
else tmp[k++]=b[j++];
}
while(i<=mid)tmp[k++]=b[i++];
while(j<=r)tmp[k++]=b[j++];
for(i=l;i<=r;i++)b[i]=tmp[i];
}
void solve(){
for(int i=1;i<=n;i++)b[i].id=i,b[i].val=a[i];
merge_sort(1,n);
for(int i=1;i<=n;i++)ans[b[i].id]=i;
}
void update(int x,int v){
int i;
if(a[x]<v){
for(i=ans[x]+1;i<=n;i++){
if(b[i].val>v||b[i].val==v&&b[i].id>x)break;
ans[b[i].id]--;
b[i-1]=b[i];
}
i--;
b[i].val=v,b[i].id=x;
ans[x]=i;
}
else if(a[x]>v){
for(i=ans[x]-1;i;i--){
if(b[i].val<v||b[i].val==v&&b[i].id<x)break;
ans[b[i].id]++;
b[i+1]=b[i];
}
i++;
b[i].val=v,b[i].id=x;
ans[x]=i;
}
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
read(n);
int q;
read(q);
for(int i=1;i<=n;i++)read(a[i]);
solve();
for(int op,x,v;q--;){
read(op);
if(op==1){
read(x),read(v);
update(x,v);
a[x]=v;
}
else{
read(x);
cout<<ans[x]<<'\n';
}
}
return 0;
}
第三题 网络连接(network)
题意:
给出一些长度在
25
25
25 以内的字符串
s
s
s ,要求输入进来合法。
分为服务机和用户机,需要满足一系列条件(写不下去了,自己去看题面吧)。
题解:
堪称小型阅读理解加模拟题,不过和去年儒略日相比还是容易拿分的。
我们进行分类讨论:
首先格式不满足的都给它扔了:
主要是几段的情况和符号的情况,数字大小的情况和前导零的情况。暴力处理一下即可。判断条件,欢乐输出
E
R
R
ERR
ERR 。
①:
S
e
r
v
e
r
Server
Server
判断是否和前面的服务机撞机了,欢乐输出
F
A
I
L
FAIL
FAIL 。
若都满足,则输出
O
K
OK
OK 。
②
判断是否和客户机撞机或者找不到服务机,欢乐输出
F
A
I
L
FAIL
FAIL 。
若都满足,则输出
O
K
OK
OK。
so,水题+1。
(如果有其他情况本人未考虑到的,欢迎留言,本人会进行更改)
参考代码
//扒了同学代码,我代码出了点小问题还没发现错误[滑稽]
//他还考虑了时间复杂度问题,用字典树优化了一下复杂度,实测不写字典树也能过。
#include<bits/stdc++.h>
using namespace std;
//By Spy_Savior
template<class T>inline void read(T&x){
x=0;
char c=getchar(),last=' ';
while(!isdigit(c))last=c,c=getchar();
while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(last=='-')x=-x;
}
const int MAXN=1e3+5,MAXM=30;
char op[MAXM],ad[MAXN];
int n,num[10];
int findNum(int begin){
if(begin>n)return -1;
while(begin<=n&&isdigit(ad[begin]))begin++;
return begin;
}
int getNum(int begin,int end){
int ret=0;
for(int i=begin;i<end;i++)ret=(ret<<3)+(ret<<1)+(ad[i]^48);
return ret;
}
bool checkAd(){
int begin=1,end;
for(int i=1;i<=3;i++){
end=findNum(begin);
if(end==-1)return false;
if(ad[end]!='.')return false;
if(end-begin>1&&ad[begin]=='0'||begin==end||end-begin>3)return false;
num[i]=getNum(begin,end);
if(num[i]>255)return false;
begin=end+1;
}
end=findNum(begin);
if(end==-1)return false;
if(ad[end]!=':')return false;
if(end-begin>1&&ad[begin]=='0'||begin==end||end-begin>3)return false;
num[4]=getNum(begin,end);
if(num[4]>255)return false;
begin=end+1;
end=findNum(begin);
if(end==-1)return false;
if(end-begin>1&&ad[begin]=='0'||begin==end||end-begin>5)return false;
num[5]=getNum(begin,end);
if(num[5]>65535)return false;
return end==n+1?true:false;
}
//Trie Tree
struct Node{map<int,int>nxt;int num;}Trie[MAXN<<2];
int cnt=1,tot=0;
bool insert(){
int x=0,last=cnt;
for(int i=1;i<=5;i++){
if(!Trie[x].nxt[num[i]])Trie[x].nxt[num[i]]=cnt++;
x=Trie[x].nxt[num[i]];
}
if(last==cnt)return false;
Trie[x].num=tot;
return true;
}
int find(){
int x=0;
for(int i=1;i<=5;i++){
if(!Trie[x].nxt[num[i]])return -1;
x=Trie[x].nxt[num[i]];
}
return Trie[x].num;
}
int main()
{
freopen("network.in","r",stdin);
freopen("network.out","w",stdout);
int T;read(T);
for(int tmp;T--;){
tot++;
scanf(" %s",op+1),scanf(" %s",ad+1);
n=strlen(ad+1);
if(!checkAd())puts("ERR");
else if(op[1]=='S'){
if(insert())puts("OK");
else puts("FAIL");
}
else{
tmp=find();
if(tmp!=-1)cout<<tmp<<'\n';
else puts("FAIL");
}
}
return 0;
}
//色维yyds
第四题 小熊的果篮(fruit)
题意:
有若干个块,每次删除每个块的第一个元素(没有则不删),删去后每个相同颜色块合并。
题解:
题目还是很水的,直接暴力能喜获
30
30
30 分,即对每次操作进行模拟。
如果你想要拿到更高的分,需要使用链表和结构体来处理每次合并,在某水果块取完后将前后水果块链接,并将相同水果合并成同一个块。也可以使用vector模拟,储存每一块的块头(在有元素的情况下)。
这里我使用了两个数组来维护每个块的变化,再不断更新,除非没有元素了,每次将当前的块头输出即可。
so,水题+99999。
参考代码:
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,a[MAXN],l[MAXN],r[MAXN];
vector<int> b;
int main()
{
scanf("%d", &n);
a[0]=a[n+1]=-1;
r[0]=1,l[n+1]=n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]!=a[i-1])
b.push_back(i);
l[i]=i-1,r[i]=i+1;
}
while(r[0]!=n+1)
{
vector<int> tmp;
for(int i=0;i<b.size();i++)
{
printf("%d ",b[i]);
int u=l[b[i]],v=r[b[i]];
r[u]=v,l[v]=u;
if(a[b[i]]!=a[u] && a[b[i]]==a[v])
tmp.push_back(v);
}
puts("");
b=tmp;
}
return 0;
}
普及组总结
今年题目还是水。。。题目偏简单,所以分数线较高。
本人今年第一次参加
C
S
P
CSP
CSP 第二轮机试,感觉没发挥出来,每道题都只要动一动脑子就能写出来。。。甚至脑袋发热直接复制大样例测试导致没复制全,以为代码出来问题强行改成暴力做法白丢48分,哎,普及组可以说是翻车了!
S组题解
先挂在这里,慢慢补上去。
FLAG!!!