CSP-J模拟赛3—总结
_Luk_
·
·
个人记录
CSP-J模拟赛3—总结
2025.10.3
王语曦
方法:
|
方法 |
考场得分 |
考场用时 |
| T1 |
暴力 |
100pts |
35min |
| T2 |
二分 |
50pts |
45min |
| T3 |
暴力 + 二分 |
24pts |
35min |
| T4 |
搜索 + 数学 |
0pts |
25min |
丢分原因:
T1:
#### T2:
$50pts$,在考试的时候直接暴力去做,看到了数据特别大但是想不到二分做法(大概是不够熟练吧),于是直接暴力去做,只得了一半的分,后面想过优化但是忘记怎么优化了。
#### T3:
$24pts$,骗分得的。在考试的时候写了代码,调试的时候电脑突然卡了,dev 也点不动,保存不了,我就简单记了一下思路然后重启了,结果重启之后想不起来思路了(红温),那个时候时间也不多了,我就先去思考最后一道题目了。
#### T4:
$0pts$,考试的时候题面有点没读懂,就理解错了,所以代码思路也就错了,下来之后找其他人问了一下,大概知道了题目意思也想了一种方法 dfs。
### T1:[number](http://bbcoj.cn/contest/1013/problem/T1/full-screen) 优美的数
#### 题意:
题面意思就是只要这个数中含有数字 $7$,或者是 $7$ 的正整数倍就是个优美的数,求第 $i$ 个优美的数。
#### 思路:
直接暴力,他的 $n \le 2021$,完全不用担心超时,直接从第一个优美的数到第 $2021$ 个优美的数算出来,再输出就可以了,非常的 $easy$。
#### AC Code:
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=2030;
int a[N],k=0;
vector<int> v; //是优美数就存进来
bool HS(int m){
while(m){
int n=m%10;
if(n==7) return true;
m/=10;
}
return false;
}
void ym(){
for(int i=1;i<=5477;i++){
if(v.size()==2021) return ;
if(HS(i)||i%7==0) v.push_back(i);
}
}
int main(){
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int T;cin>>T;
int M=T;
while(M--){k++;cin>>a[k];}
ym();
for(int i=1;i<=T;i++) cout<<v[a[i]-1]<<'\n';
return 0;
}
```
### T2:[xor](http://bbcoj.cn/contest/1013/problem/T2/full-screen) 异或
#### 题意:
给 $n$ 个数和一个数 $k$,求这 $n$ 个数之中两数异或值为 $k$ 的不同组的个数。
#### 思路:
$50pts$:先给数组 $a$ 排一下序,然后直接双重循环遍历数组 $a$,算 $a_i$ 和 $a_j$ 的异或值是否为 $k$,是的话 $ans++$,最后输出 $ans \times 2$ 就可以了(每组两个数字可以互换)。
$100pts$:根据异或的性质,可以知道 $a \oplus b = c$ 等价于 $a \oplus c = b$,所以就不用专门费工夫去求 $b$ 的值了,然后二分查找,靠左和靠右分别找出两个下标,最后输出 $ans+=p1-p2+1$ 就可以了。
#### AC Code:
```cpp
#include<bits/stdc++.h>
using namespace std;
int a[1000005],n,k;
int lower(int f){ //靠左查找
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
if(a[mid]>=f) r=mid;
else l=mid+1;
}
if(a[l]!=f) return 1;
else return l;
}
int upper(int f){ //靠右查找
int l=1,r=n;
while(l<r){
int mid=l+r+1>>1;
if(a[mid]>f) r=mid-1;
else l=mid;
}
if(a[l]!=f) return 0;
else return l;
}
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
cin>>n>>k;int ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
int f=a[i]^k; //a^c=b 等价于 a^c=b
int p1=upper(f);
int p2=lower(f);
ans+=p1-p2+1;
}
cout<<ans;
return 0;
}
```
### T3:[separation](http://bbcoj.cn/contest/1013/problem/T3) 分身术
#### 题意:
求一个区间地址,使 $min(a[i],a[j])$,和 $max(b[i],b[j])$ 的差值大于等于 $k$,同时使得 $i$ 与 $j$ 的差最小。
#### 思路:
$24pts$:骗分,输出 `So Sad!` 即可。
$30pts$:按照题意直接模拟即可,在老师的数据下可以获得 $36pts$。
$60pts$:把 $30pts$ 的循环优化一下,就可以有 $60pts$ 了。
$80pts$:用一个 dp 去选择最优的地址,然后用一个二分,确定 $l$ 的地址,去便利 $r$ 的地址,得到 $ans$ 的最小值。(在老师的数据下可以 AC)。
$100pts$:用两个双向队列去存取数组中的元素,然后找到最小的 $t$。
#### $100pts$(实际上是 $80pts$)Code:
```cpp
#include<bits/stdc++.h>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<=(b);i++)
int n,k;
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*f;
}
const int N=1e6+5;
int dp[2][N][25];
//dp[0][][] => a 序列
//dp[1][][] => b 序列
bool check(int l,int r){ //左端点和右端点
int x=log2(r-l+1);
int minn=min(dp[0][l][x],dp[0][r-(1<<x)+1][x]);
int maxn=max(dp[1][l][x],dp[1][r-(1<<x)+1][x]);
return minn+k<=maxn;
}
int a[N],b[N],ans=1e9+5;
int main(){
freopen("separation.in","r",stdin);
freopen("separation.out","w",stdout);
n=read(),k=read();
_for(i,1,n) dp[0][i][0]=read();
_for(i,1,n) dp[1][i][0]=read();
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
dp[0][i][j]=min(dp[0][i][j-1],dp[0][i+(1<<j-1)][j-1]); //最小值
dp[1][i][j]=max(dp[1][i][j-1],dp[1][i+(1<<j-1)][j-1]); //最大值
}
}
//枚举左端点,二分右端点
_for(i,1,n){
int l=i,r=n+1; //r => 合法的序列
while(l<r){
int mid=(l+r)>>1;
if(check(i,mid)) r=mid;
else l=mid+1;
}
if(r<=n) ans=min(ans,r-i+1);
}
if(ans>n) cout<<"So Sad!";
else cout<<ans;
return 0;
}
```
#### AC Code:
```cpp
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*f;
}
int a[MAXN],b[MAXN],ans=1e9;
deque<int> dmin,dmax;
#define _for(i,a,b) for(int i=(a);i<=(b);i++)
int main(){
freopen("separation.in","r",stdin);
freopen("separation.out","w",stdout);
int n,k;n=read(),k=read();
_for(i,1,n) a[i]=read();
_for(i,1,n) b[i]=read();
int r=1; //区间右边的指针
_for(l,1,n){
while(r<=n&&(dmin.empty()||a[dmin.front()]+k>b[dmax.front()])){
while(!dmin.empty()&&a[dmin.back()]>a[r]) dmin.pop_back();
dmin.push_back(r);
while(!dmax.empty()&&b[dmax.back()]<b[r]) dmax.pop_back();
dmax.push_back(r);
r++;
}
if(a[dmin.front()]+k<=b[dmax.front()]) ans=min(ans,r-l);
if(!dmin.empty()&&dmin.front()==l) dmin.pop_front();
if(!dmax.empty()&&dmax.front()==l) dmax.pop_front();
}
if(ans>n) cout<<"So Sad!";
else cout<<ans;
return 0;
}
```
### T4:[tree](http://bbcoj.cn/contest/1013/problem/T4/full-screen) 种树
#### 思路:
用 dfs 去搜索(只是想到了这种思路,还没有去实现)。