1. 程式人生 > 其它 >CSP-J 2021遊記

CSP-J 2021遊記

這次考試心態還可以,但是四道題只對了一道,兩道超時,一道答案錯誤。做得不好,沒有達到預期。除了第三題之外,其他題對拍全過,時間複雜度太高。

### 洛谷自測分數
|分糖果|插入排序|網路連線|小熊的果籃|總分|
|--|--|--|--|--|
|AC 100|TLE 72|WA 25|TLE 70|267|

---
#### 1.分糖果
這道題做的還不錯,想的時候沒有花太多時間。

L和R資料範圍達到了恐怖的$10^9$,很明顯不能暴力,只能考慮$O(1)$。
首先求出一個數$t$,$t$是$R$左邊的第一個滿足$t$ $mod$ $n=n-1$的數。
然後再判斷,這個$t$在不在$L$到$R$的區間內。
如果在,答案為$t$ $mod$ $n$。
如果不在,可以證明,答案為$R$ $mod$ $n$。

##### 考場程式碼(AC程式碼)
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,l,r;
scanf("%d%d%d",&n,&l,&r);
int t=(n-1-l%n)+l;
if(t<=r){
printf("%d",t%n);
}else{
printf("%d",r%n);
}
}
```

---
#### 2.插入排序
考場上找出規律:可以用結構體+sort,先按照數值排序,數值相同的情況下按編號排序,於是就有了考場上$O(1)$修改的程式碼。
但是我忽略了一點,修改次數不超過5000,然後就悲劇了……
下次一定要認真審題,不要忽略題目給你的任何一點提示或要求。

##### 考場程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
int n,q;
struct node{
int x,y;
bool operator <(const node &t)const{
return x<t.x||(x==t.x&&y<t.y);
}
}b[8001];
int a[8001];
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=q;i++){
// memset(b,0,sizeof(b));
// memset(num,0,sizeof(num));
int t,x,y;
scanf("%d",&t);
if(t==1){
scanf("%d%d",&x,&y);
a[x]=y;
}
if(t==2){
scanf("%d",&x);
for(int j=1;j<=n;j++){
b[j].x=a[j];
b[j].y=j;
}
sort(b+1,b+n+1);
for(int j=1;j<=n;j++){
if(b[j].y==x){
printf("%d\n",j);
}
}
}
}
}
```

##### 【正解】
既然題目說了,排序不產生影響,那麼我們不必在陣列中進行排序,可以用$vector$。
其次,每次只修改一個數,也就意味著之前的排序結果可以重用,從而降低時間複雜度。
對於每一次修改,我們只需要:
+ 在$vector$中找到並刪除這個數
+ 往$vector$中推入一個新的數,並維護數列單調性。
但是,這個數列是單調遞增的,於是可以使用二分來降低時間複雜度。
於是就有了優秀的$O(nlogn)$的AC程式碼:

##### AC程式碼
```cpp
//參考網址:https://www.cnblogs.com/chy12321/p/15456919.html#%E9%A2%98%E8%A7%A3
#include<bits/stdc++.h>
using namespace std;
int n,q;
struct node{
int v,id;
bool operator <(const node &t)const{
return v<t.v||(v==t.v&&id<t.id);
}
}a[8001];
vector<node> f;
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&a[i].v);
a[i].id=i;
f.insert(lower_bound(f.begin(),f.end(),a[i]),a[i]);
}
int tt,x,v;
for(int i=1;i<=q;i++){
scanf("%d",&tt);
if(tt==1){
scanf("%d%d",&x,&v);
f.erase(lower_bound(f.begin(),f.end(),a[x]));
a[x].v=v;
f.insert(lower_bound(f.begin(),f.end(),a[x]),a[x]);
}else{
scanf("%d",&x);
printf("%d\n",lower_bound(f.begin(),f.end(),a[x])-f.begin()+1);
}
}
}
```
---
#### 網路連線
赤裸裸的大模擬,碼農題,思路、判斷什麼的看題就行。
考場上一次性打完了,樣例只有一個沒過,實在調不出來了。
##### 考場程式碼
```cpp
#include<bits/stdc++.h>
using namespace std;
int n;
int a[101],nf,nk,b[101];
int vis[2001][101],vis1[2001][101],fuwu[2001];
bool check(int x,int y,int z,int k,int w,char a1,char a2,char a3,char a4){
if(a1=='.'&&a2=='.'&&a3=='.'&&a4==':'&&x>=0&&x<=255&&y>=0&&y<=255&&z>=0&&z<=255&&k>=0&&k<=255&&w>=0&&w<=65535){
return true;
}
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
memset(a,0,sizeof(a));
char t;
bool flag1=true;
while(1){
t=getchar();
if(t=='S'){
flag1=true;
break;
}
if(t=='C'){
flag1=false;
break;
}
}
bool flag2=false,flag3=false;
int x=0,cnt=0,cnt1=0;
while(1){
if(t=='\n'&&flag3==false){
flag3=true;
}
if(flag3==true&&t=='\n'){
break;
}
t=getchar();
if(t>='0'&&t<='9'){
flag2=true;
}
if(flag2){
if(t>='0'&&t<='9'){
x=x*10+(int)(t-'0');
}else{
b[++cnt1]=t;
flag2=false;
a[++cnt]=x;
x=0;
}
}
}
if(!check(a[1],a[2],a[3],a[4],a[5],b[1],b[2],b[3],b[4])){
printf("ERR\n");
}else{
if(flag1){
bool flag4=true;
for(int j=1;j<=nf;j++){
int sum=0;
for(int k=1;k<=5;k++){
if(vis[j][k]==a[k]){
sum++;
}
}
if(sum==5){
flag4=false;
break;
}
}
if(!flag4){
printf("FAIL\n");
}else{
printf("OK\n");
nf++;
for(int j=1;j<=5;j++){
vis[nf][j]=a[j];
}
}
}else{
bool flag4=true;
int tt;
for(int j=1;j<=nf;j++){
int sum=0;
for(int k=1;k<=5;k++){
if(vis[j][k]==a[k]){
sum++;
}
}
if(sum==5){
flag4=false;
tt=j;
break;
}
}
if(flag4){
printf("FAIL\n");
}else{
bool flag5=true;
int pp;
for(int j=1;j<=nk;j++){
int sum=0;
for(int k=1;k<=5;k++){
if(vis1[j][k]==a[k]){
sum++;
}
}
if(sum==5){
flag5=false;
pp=j;
break;
}
}
if(!flag5){
printf("%d\n",fuwu[pp]);
}else{
nk++;
fuwu[nk]=tt;
printf("%d\n",tt);
}
}
}
}
}
}
```
至今仍未AC……

---
#### 小熊的果籃
考試時的想法就是直接模擬,輸出過的就標記。
```cpp
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200001];
int main(){
scanf("%d",&n);
int la=-1,n1=n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(la!=a[i]){
la=a[i];
printf("%d ",i);
a[i]=2;
n1--;
}
}
printf("\n");
while(n1>0){
la=-1;
bool flag=false;
for(int i=1;i<=n;i++){
if(a[i]==2)continue;
if(la!=a[i]){
la=a[i];
printf("%d ",i);
flag=true;
a[i]=2;
n1--;
}
}
if(flag)printf("\n");
}
}
```
這樣寫肯定超時,目前還未AC。

---

## 總結一下
這次考試後三道題某谷給出的標籤都是模擬,暴力,第一題是數學,數論。
三道模擬題寫掛,很不應該,應該好好反思,為什麼沒有優化時間複雜度?第三題這種碼農題練得也不多,總的來說情況不好,下次加油。

---
##### 網上發現兩個題解,可以參考參考:
[第一個](https://blog.csdn.net/Code_Shark/article/details/120934954)
[第二個](https://www.cnblogs.com/chy12321/p/15456919.html)