欢迎光临
我们一直在努力

Csp-j 2021复赛真题题解

1,分糖果

分析:本题是初赛第一题,按照以往经验,这一题一般是小模拟题或者数学题。

我们可以发现本题的大意是要求我们那一些糖果并分给n个人,直到糖果数小于n。

看得出来,本题的考察范围是数学计算,如果我们能找到通用公式计算,就可以避免模拟。其核心思路就是取余和特判,如果l和r达到某某要求,则使用特别公式。

解题思路:我们可以分析一下,由于题目的特殊性质,这道题我们直接使用区域公式计算的同时,也有某些情况无需计算,只需直接输出n即可。

题目的核心是在[l,r]区间中找一个数k,使得k % n最大。

当l和r于同一个周期内,说明其中所有值%n的值都相同,我们就可以输出r%n代表所以区间内值的最优。、

反之,不在同一个周期内,则小朋友会一直那直到小于n,这样最大只有n-1,就输出理论极限n-1。

我们来模拟下样例,证明思路。

7 16 23,16和23不在同一周期,则输出理论极限n-1,即6,正确。

样例通过证明我们的思路正确,接下来构造代码:

#include<bits/stdc++.h>
using namespace std;
long long n,l,r;//初始化
int main(){
cin>>n>>l>>r;//输入
if (l/n!=r/n)//跨越周期,输入理论极限
cout<<n-1;
else cout<<r%n;//在同一个周期内,余数随着数字的增大而严格单调递增,商相同,我们只需输出区间最大值r取余n的区间公共余数即可
return 0;
}

2,插入排序

分析:本题要求输入一个数组,然后进行q次查询,排序或改值。

不难看出来,这是一道大模拟题或者交互题。

每次进行查询,然后进行相对修改即可。

注意一个要点,本题的排序操作不可以永久修改。

然后我们对样例模拟一下,证明我们的思路。

 3 4为首先输入的两个。

3 2 1为输入的序列。

接下来4行输入。

2 3,插入排序并输出原来第3个元素的新位置,由于进行了排序,a被排到了第一个,输出1。

1 3 2,3的位置变为2,现在是3 2 2。

2 2,2的位置是2,经排序后相对位置不变,为1。

2 3,3的位置为2,经排序后相对位置不变,为2。

样例证明我们的猜想是正确的,接下来我们来构建代码:

#include<bits/stdc++.h>
using namespace std;
struct Node{
int m;
bool f;//定义标记结构体
};
int n,q,a[8020];//初始化
int main(){
cin>>n>>q;
for (int i = 1;i <= n;i++){
cin>>a[i];//输入
}
while (q–){//q次查询
int w;cin>>w;
if (w == 1){//路径1
int x,v;
cin>>x>>v;
a[x] = v;//修改
continue;//跳过
}
int x;cin>>x;Node b[8020];//标记数组
for (int i = 1;i <= n;i++){
b[i].m = a[i];
b[i].f = false;//标记给定下标
}
b[x].f = true;//修改
for (int i = 1;i <= n;i++)
for (int j = i;j >= 2;j–)
if (b[j].m < b[j-1].m)
swap(b[j-1],b[j]);//插入排序
for (int i = 1;i <= n;i++){
if (b[i].f == true){//查找下标
cout<<i<<endl;
break;
}
}
}
return 0;
}

然而我们发现这题因为样例很大,此做法只有50分。

接下来我们来优化。

这道题的TLE罪魁祸首是排序,导致代码复杂度变为O(n^n),如果我们能够修改代码变为去除循环内的代码,则复杂度为O(n),就可以通过。

我们可以将插入排序转换为局部的修改,这样可以达到线性复杂度。

我们可以新建一个映射,这个映射包含排序前的内容,可以保存信息。

然后我们每次修改只需对表修改,对局部进行插入即可。

每次我们进行1操作时,更新原数组,更新复制数组,然后扫描数组,找到局部可插入位置插入,并重新映射,修改原数组即可,这样2操作只要查询即可。

#include<bits/stdc++.h>
using namespace std;
int n,q;
vector<pair<int,int>> a;//原数组
vector<pair<int,int>> temp;复制数组
int main(){
cin>>n>>q;
for (int i = 0;i < n;i++){//输入
int x;cin>>x;
a.push_back({x,i});//插入信息
}temp = a;
sort(temp.begin(),temp.end());//排一次序保证以后局部有序
vector<int> pos(n);//映射数组
for (int i = 0;i < n;i++)
pos[temp[i].second]=i;//映射
while (q–){//多次操作
int k;
cin>>k;
if (k == 1){
int x,y;cin>>x>>y;
x–;
int p=pos[x];//找到旧元素在temp中的位置
for(int i=p;i<temp.size()-1;i++)
temp[i] = temp[i+1];//后面的元素整体前移,覆盖掉旧元素
temp.pop_back();//删除尾部多余元素
for (int i = p;i < temp.size();i++)
pos[temp[i].second]=i;//更新映射
a[x].first = y;//更新原数组值
int ispos = 0;//线性扫描,找到新值应该插入的位置
while (ispos<temp.size()&&temp[ispos]<a[x])
ispos++;
temp.push_back({0,0});//更新复制数组
for (int i=temp.size()-1;i>ispos;i–)
temp[i] = temp[i-1];//从后往前移动元素,腾出ispos位置
temp[ispos] = a[x];//插入新元素
for (int i = ispos;i < temp.size();i++)
pos[temp[i].second] = i;//建立新映射
}else{
int x;//因为每次修改都更新,所以这里只需直接查询即可
cin>>x;
x–;
cout<<pos[x]+1<<endl;//下标对齐
}
}
}

3,网络连接

分析:不愧是绿题,连描述都那么长。

实则翻译过来,只有几句话。

核心意思:给定任意个机器,每台机器有一个独立ip号。

若是服务器,则新建服务并查看,如果格式正确且ip没有被占用,则是一个可用的端口号,注册新服务器。

若是客户端,则判断加入的ip是否存在,如果是,则加入并输出改ip服务器的编号,否则报错。

每一台机器都有一个独立编号,比上一个+1。

这大概就是题目意思。

我们来模拟下样例。

第一个,server且格式正确,建立服务器。

第二个,ip被占用,所以输出FAIL。

第三个,ip正确,加入并输出编号。

第四个,不存在服务器,所以报错FAIL。

第五个,格式错误,直接报ERR。

这就是我们的想法,完全一样!

接下来展示代码:

#include<bits/stdc++.h>
using namespace std;
struct Node{
string j;//状态结构体
int id;
};
int n;
bool check(string ip){
int cnt1 = 0,cnt2 = 0;
for (int i = 0;i < ip.size();i++){
if (ip[i] == '.')
cnt1++;
if (ip[i] == ':')//统计符号数量
cnt2++;
}
if (cnt1 != 3)
return false;
if (cnt2 != 1)
return false;//不对剔除
string be = "";//线性字符串扫描(be用来存之前截下来的)
for (int i = 0;i < ip.size();i++){//扫描
if (ip[i] == '.' || ip[i] == ':'){//是符号
if (be.empty()) return false;//符号前的内容是空,剔除
if (be.size() > 1 && be[0] == '0')
return false;//前导0太多,剔除
for (int j = 0;j < be.size();j++)
if (!isdigit(be[j])) //不是数字
return false;//剔除
long long num = stoll(be);//字符串转long long
if (num < 0 || num > 255) //超出范围,剔除
return false;
be = "";
}else be += ip[i];//累加以便判断
}
if (be.empty()) return false;//最后一段为空,剔除
if (be.size() > 1 && be[0] == '0') return false;//前导0太多,剔除
for (int j = 0;j < be.size();j++)
if (!isdigit(be[j])) return false;//不是数字
long long num = stoll(be);//转换类型
if (num < 0 || num > 65535) return false;//范围不符合
return true;//正确的地址
}
pair<bool,int> in(string ip,vector<Node> a){//当前地址是否存在
for (int i = 0;i < a.size();i++)
if (ip == a[i].j)
return {true,a[i].id};//返回
return {false,-1};
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);cin>>n;
vector<Node> a;//服务器数组
int i = 1;//编号
while (n–){
string m;cin>>m;
string ip;cin>>ip;//输入
if (!check(ip))//不是正确地址
cout<<"ERR"<<endl;
else if (m == "Server"){//服务器
auto ans = in(ip,a);//当前地址状态
if (ans.first){
cout<<"FAIL"<<endl;//已经存在,这次申请失效
}else{
cout<<"OK"<<endl;//申请成功
a.push_back({ip,i});//更新
}
}else if (m == "Client"){//客户端
auto ans = in(ip,a);
if (ans.first)//存在
cout<<ans.second<<endl;//成功访问,输出编号
else cout<<"FAIL"<<endl;//失败,没有地址
}
i++;//编号累加
}
return 0;
}

完成了,现在分数来到了300分(一等奖)!

赞(0)
未经允许不得转载:171主机测评 » Csp-j 2021复赛真题题解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址