2023CSP复赛/NOIP备战模拟赛复盘集合
2023 10 03 CSP-J 模拟赛 复盘
这次模拟赛考的特别差,只有160。
T1:一上来,虽然不那么打卡,但也挺简单,然后五分钟写完,对了对样例,对了,走人。
T2:需要在\(O(n logn)\)或者\(O(n)\)的时间复杂度求出每一个区间被覆盖的区间,这要怎么求啊?我想了半天也只知道\(O(n^2)\)怎么做,然后发现一个小时过去了,同学们都貌似切掉了这一题,开始往后写了。感觉有点慌,普及T2没切出来……
T3:是挺水的,洪水填充+\(O(1)\)回答搞定。
T4:是一道大模拟+贪心,手动列举if的条件,列举了很多发现越来越多情况。于是就换了一种思路,写啊写,终于写完了100行。自己出了几个样例结果都成为了heck数据,有调了调,几个heck也过掉了。于是又出了十几个数据,貌似都对了。然后剩下时间十分钟,回去先把T2的暴力写完就没时间了。
最后成绩:
0+60+100+0
第一题竟然能暴0,这也是很奇怪的。发现是有个地方没有+1。
#include<bits/stdc++.h>
using namespace std;
string s;
int m;
int main(){
cin >> s;
cin >> m;
while(m--){
int a,b,c;
cin >> a >> b >> c;
a--,b--;
c %= b-a+1;
string e;
for(int i=a;i <= b;i++){
e += s[(i-c) >= a? (i-c):b-abs((i-c)-a)+1];
}
for(int i = a;i <= b;i++){
s[i] = e[i-a];
}
}cout<<s<<endl;
return 0;
}
第二题其实我是几乎想到了正解,只是最后没有时间了,其实正解也不过就是sort排个序,动态求max即可。
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define fir first
#define se second
#define ull unsigned long long
#define endl "\n"
using namespace std;
int n;
const int N = 1e5+5;
pii a[N];
int main(){
cin >> n;
for(int i = 1;i <= n;i++){
cin >> a[i].fir >> a[i].se;
}
sort(a+1,a+1+n);
int ans= 0;
int maxr=0,maxn=0;
for(int i = 1;i <= n;i++){
if(a[i].fir>a[i-1].fir){
maxr = max(maxn,maxr);
}
maxn = max(maxn,a[i].se);
if(maxr > a[i].se) ans++;
}cout<<ans;
return 0;
}
第三题过了也不意外,因为很水
#include <bits/stdc++.h>
using namespace std;
int n,m;
const int N = 1005;
char c[1005][1005];
int dx[4]={0,0,-1,1};
int dy[4]={1,-1,0,0};
int dis[N][N];
int val[N*N];
int res=0;
int cnt=0;
void dfs(int x,int y){
dis[x][y] = res;cnt++;
for(int i = 0 ;i < 4;i++){
int xx=x + dx[i];
int yy=y + dy[i];
if(xx >= 1 && xx <= n && yy>=1&&yy<=m&&c[xx][yy]=='.'&&dis[xx][yy]==0){
dfs(xx,yy);
}
}
}
int main(){
cin >> n >> m;
for(int i =1;i <= n;i++){
for(int j =1;j <= m;j++){
cin >> c[i][j];
if(c[i][j]=='*')dis[i][j] = -1;
}
}
for(int i=1;i <= n;i++){
for(int j = 1;j <= m;j++){
if(!dis[i][j]){
cnt=0;
++res;
dfs(i,j);
val[res] = cnt;
}
}
}
for(int i = 1;i <= n;i++){
for(int j =1;j <= m;j++){
if(dis[i][j]!=-1)cout<<".";
else{
int ans=1;
set<int> ve;
for(int k=0;k<4;k++){
int xx=i+dx[k];
int yy=j+dy[k];
ve.insert(dis[xx][yy]);
}
for(auto it:ve){
ans+=val[it];
// cout<<val[it]<<endl;
}
cout<<ans%10;
}
}puts("");
}
return 0;
}
第四题我都傻了,竟然那么多个数据都没有heck掉。原来在只包含4和7的输入中有一组数据”7777“是有问题的,在判断包含4,7这个输入的时候没有考虑到越界+2的问题,然后还有一个地方应该else if和if颠倒才是对的,想起来还是太粗心了,要是这两个错没有,估计我就是机房唯一一个A的。虽然说在写复盘的时候还不能提交(因为代码源在比赛后一定时间不能提交)但是我拿着周思源的暴力60分代码跑了对拍,过掉了一千多个样例,甚至根袁梓熠的正解(挂成40)的代码对拍只有一个错误,而且这个错误明显是他错了。总结一下,这种多组数据的题目实在是太坑了,一个点错就挂掉。
总结:
这次考差的原因就是第二题没有想出来(正确来说其实是我审题不仔细,把”有多少个点被覆盖“看成”每个点覆盖的区间之和“,然而我还因为把题目看成后者,写了二分加sort等等,特别复杂。。)下次还是要审题仔细,不要图一时快。这次就是因为T2挂了,因此心态有些不好,有点慌张,导致后面都比较紧张,也是T4少了贪心情况的的一个原因。。。
不过这次下午的讲评还是非常高兴能够听到(全球第四, 2015IOI金牌,CF rating TOP 1,AT rating TOP 5)的大佬杜瑜皓老师的讲课,虽说是线上,但是感觉讲的很好!
2023 10 4 CSP-J模拟赛复盘
这次只考了200
T1:很简单,直接切了
T2:贪心嘛,直接开切,尽可能把最高位变得更大就可以了
T3:也是一个贪心,预处理树的深度和子树大小,然后sort一遍,关键字是深度-子树,也就是说每一个点如果变成黑色的话,其贡献是他到根节点的距离,然后还会影响他的子树的值,所以要减去子树大小。
T4:想了一下,像是个贪心?认为是一个DP+贪心+二分?然后细想,发现不知道如何logn求一个动态区间的最大值,别说,还想到单调队列 可这是pj组啊。最后没时间了,直接N^2暴力dp出来了。。
期盼得分100+100+100+60=360
最终得分90+20+30+60=200
挂分挂的惨啊。。
T1算约数的地方挂了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
void print(int x,int y){//约分并打印
int s=min(x,y);
int flag=1;
while(flag){
flag=0;
for(int i =2;i <= __gcd(x,y);i++){
if(x%i==0 && y%i==0){
x/=i;
y/=i;
flag=1;
}
}
}
cout<<x<<"/"<<y<<endl;
}
signed main(){
int a,b,c,d;
cin >> a >> b >> c >> d;
if(a*d == c*b)cout<<"0/1";
else print(max(a*d,c*b)-min(a*d,c*b),max(a*d,c*b));
return 0;
}
T2有一些没有特判,导致RE,没仔细想,n如果很大的话,可能处理出最大值还是有剩下的n,这一点没有特判掉,但是整体思路基本上是对的。
#include <bits/stdc++.h>
using namespace std;
char c[25];
int t;
int n;
bool check(){//判断是否已经是最大值了
for(int i=1;i<strlen(c);i++)if(c[i] >c[i-1]) return true;
return false;
}
int main(){
cin >>t;
while(t--){
scanf("%s",c);
cin>>n;
if(strlen(c)==1 || !check()) {//特判掉直接输出的情况
cout<<c<<endl;
continue;
}
int j = 0;
int f=n;
while(n>0){
int maxn = c[j]-'0',maxi = j;
for(int i=j;i <= min(j+n,(int)strlen(c)-1);i++){//找最大值
if(maxn<(c[i]-'0')){
maxn = c[i]-'0';
maxi = i;
}
}
n-=maxi-j;//减去贡献
for(int i = maxi;i >= j+1;i--){
c[i]=c[i-1];
}c[j]=maxn+'0';
j++;
if(!check())break;//如果已经是最大的,break
}
cout<<c<<endl;
}
return 0;
}
T3我一开始想的是从叶子节点开始动态求max,也就是priority queue赛后找出了反例,不一定所有叶子都要取,反而直接把所有点的贡献sort一遍求前k大即可,然后因此AC。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5+5;
int n,k;
vector<int> mp[N];
int dep[N];
int son[N];
priority_queue<int> q;
void dfs(int x,int fa){//深搜预处理深度和子树大小的数组。
son[x] = 1;
for(auto it:mp[x]){
if(it==fa)continue;
dep[it]=dep[x] + 1;
dfs(it,x);
son[x]+=son[it];
}
}
signed main(){
int ans=0;
cin >> n >> k;
for(int i = 1;i <= n-1;i++){
int u,v;
cin >> u >> v;
mp[u].push_back(v);
mp[v].push_back(u);
}
dep[1]=0;
dfs(1,0);
for(int i = 1;i <= n;i++){//加入每一个点的贡献
q.push(dep[i]-son[i]+1);
}for(int i =1;i <= k;i++)ans+=q.top(),q.pop();//计算答案
cout<<ans;
return 0;
}
T4:其实只需要映射一下,就可以变成最长下降子序列,因此可以用贪心二分来\(O(n log n)\)解决掉。
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define fir first
#define se second
#define ull unsigned long long
#define endl "\n"
using namespace std;
const int N = 1E6+10;
int n;
int dp[N];
int l[N],r[N];
bool cmp(int x,int y){return x>y;}
int main(){
cin >> n;
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
l[x]=i;
}for(int i=1,x;i<=n;i++){
scanf("%d",&x);
r[l[x]]=i;
}int cnt=0;
for(int i = 1;i <= n;i++){
int pos = lower_bound(dp+1,dp+1+cnt,r[i],cmp)-dp;
dp[pos]=r[i];
cnt=max(cnt,pos);
}cout<<cnt;
return 0;
}
总结,这次考的很差,我认为还是不够细心,贪心题一定要仔细细心想条件,不要漏掉了一些条件。还有就是时间安排不妥当,想玩玩对拍,结果T3对拍浪费很多时间,而且数据生成器写的有点问题,拍的也不是很理想。
10 05 CSP-J模拟赛
考的很差,只有180
目标100+100+100+0=300
实际100+60+10+10+180
T1:过了
#include <bits/stdc++.h>
using namespace std;
int x,k;
int vis[4100];
int main(){
cin >> x >> k;
for(int i = 1;i <= k;i++){
int op,x,y;
cin >> op>>x;
if(op==1){
cin >> y;
vis[x]=1;
vis[y]=1;
}else{
vis[x]=1;
}
}
int maxn = 0,minn = 0;
for(int i = 1;i <= x-1;i++){
if(!vis[i]) maxn++;
}for(int i = 1;i <= x-1;i++){
if(!vis[i] && !vis[i+1]){
i++;minn++;
}else if(!vis[i]){
minn++;
}
}cout<<minn << " "<<maxn;
return 0;
}
T2:很容易想到两遍差分+前缀和,但是我是个傻子也不过分,开了long long 结果printf("%d"),改完后AC。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int n,m,k;
int a[N];
struct node{
int l,r,d;
}s[N];
int sao[N];
int qzh[N];
signed main(){
cin >>n >> m >>k;
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
}for(int i = 1;i <= m;i++){
scanf("%lld%lld%lld",&s[i].l,&s[i].r,&s[i].d);
}
for(int i = 1;i <= k;i++){
int x,y;
scanf("%lld%lld",&x,&y);
sao[x]++;
sao[y+1]--;
}
for(int i=1;i<=m;i++){
sao[i]+=sao[i-1];
s[i].d *= sao[i];
// cout<<sao[i]<<" ";
}
for(int i = 1;i <= m;i++){
qzh[s[i].l] += s[i].d;
qzh[s[i].r+1] -= s[i].d;
}
for(int i = 1;i <= n;i++){
qzh[i] += qzh[i-1];
}
for(int i = 1;i <= n;i++){
printf("%lld ",qzh[i] + a[i]);
}puts("");
return 0;
}
/*
记得开long long
扫描线思想:
维护x和y
计算出每一个操作需要进行多少次。
再乘上权值
再对l,r跑一遍前缀和。
*/
T3:我把check里想复杂了,其实很简单可解决。
#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[10005];
bool check(double x){
double num1= 0, num2= 0;
for (int i = 1; i <= n; i++){
if (a[i] >= x)
num1 += a[i] - x;
else
num2 += x - a[i];
}
return num1* (1 - k / 100.0) >= num2;
}
int main(){
cin >> n >> k;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
double l = 0, r = 1000, mid = (l + r) / 2;
while (r - l > 1e-9){
mid = (double)(l + r) / 2;
if (check(mid)) {
l = mid+1e-9;
}
else {
r = mid;
}
}
printf("%.9lf", l);
return 0;
}
T4:我原本想写贪心,结果没有想到贪心的思路,后面写了优先队列+dp,时间复杂度\(O(nlogn)\)完全可以过,然后比赛结束了还没有调出来,我以为样例没过是0分,所以交了一个-1的代码,最后发现原本的DP可以拿到40分(bei,痛失30分。。。
还有就是题意有点理解错了,我以为有有限个轮胎,原来只有一个,要是考场上知道这一点,特判+DP,还有50分。不过主要还是dp写错了,因为我去按照无限个轮胎来跑了,所以错了,整体思路也不对了。
正解:因为寒冷的情况必须用耐寒轮胎,所以如果寒冷时间>k,则输出-1。然后我们将耐寒设为0的段落,正常普通轮胎设成1的段落,贪心思路就是如果我们将1换成0,也就是说在普通时间用耐寒轮胎,可以省去2次换胎。当然sort一下,每一次选最短的段落,肯定是最优的,然后最后再特判一下最后结尾的情况就可以AC了。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, k, t[N];
int main() {
cin >> n >> k;
int res= 0;
for(int i = 1; i <= n; i++) {
cin >> t[i]; if(t[i] < 0) res++;
}
if(res> k) {
cout << "-1";
return 0;
}
k -= res;
vector<int> ve;
int ls= 2e9;
for(int i = 1; i <= n; i++) {
if(t[i] < 0) continue;
int j = i;
for(; j <= n && t[j] >= 0; j++);
if(j <= n && i > 1) ve.push_back(j - i);
else if(i > 1) ls= j - i;
i = j;
}
int ans = 0;
if(res> 0) ans = 2 * (ve.size() + 1);
sort(ve.begin(), ve.end());
for(auto i : ve) {
if(k >= i) {
k -= i;
ans -= 2;
}
}
if(res > 0 && k >= n - ls - 1) {
ans--;
}
cout << ans;
return 0;
}
总结:
这次又是因为不开long long 见祖宗,把题目想难了,挂分挂的很惨。
就是心态上有点紧张,前几次都是挂分挂的特别惨,所以这次前面几道题切的很快,就为了能够最后一题打出点东西来,结果最后一题题意理解错误,前面的也是细节挂掉,一举两失,下次还是要读题更加仔细,等到思考完在做,不然写完才发现思路错了,那就时间不够了,争取下一次不要挂分,要挂分也不要挂多于50,目标:350+!!!!!
2023 10 06 CSP-J模拟赛复盘
这次考的很差,只有200。主要原因因为T2爆零了。
目标得分:100+100+0+100
实际得分:100+0+0+100
T1:
AC签到题
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n,k;
int a[N];
signed main(){
cin >> n >> k;
int res=n;
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
if(a[i]==k)res--;
}
int cnt=0;
while(res){
sort(a+1,a+1+n);
for(int i = 1;i <= n;i++){
int j=i;
for(;j <= n && a[i] == a[j];j++);
j--;
a[i]++;
if(a[i]==k)res--;
i=j;
}
++cnt;
}cout<<cnt;
return 0;
}
T2:
vector遍历写成只到<size-1,在两步跳着遍历的时候写成++。。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int n,k;
int a[N];
bool cmp(int a,int b){
return a>b;
}
signed main(){
int t;
cin >> t;
while(t--){
cin>>n;
vector<int> ji,ou;
for(int i = 1;i <= n;i++){
scanf("%lld",&a[i]);
if(a[i]&1){
ji.push_back(a[i]);
}else{
ou.push_back(a[i]);
}
}
sort(ji.begin(),ji.end(),cmp);
int ans=0;
for(auto it:ou){
if(it>0)ans+=it;
}
ans+=ji[0];
for(int i = 2;i < ji.size();i+=2){
if(ji[i]+ji[i-1] > 0){
ans+=ji[i]+ji[i-1];
}
}
cout<<ans<<"\n";
}
return 0;
}
T3:赛时没哟想出来
T4:想了一想,发现遍历n个点,然后再DFS,然后最后算出每一个点到达次数的组合数,时间复杂度\(O(nm)\),然后写完对拍之后,几万个数据都过掉了,最后也是AC了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3005;
int n,m;
vector<int> mp[N];
int vis[N];
int dp[N];
int dep[N];
void dfs(int x,int step){
if(step==2)return ;
for(auto it:mp[x]){
if(vis[it])continue;
// cout<<it<<' '<<step+1<<endl;
dep[it]=max(dep[it],step+1);
if(step+1==2)dp[it] ++;
vis[it]=1;
dfs(it,step+1);
vis[it]=0;
}
}
signed main(){
cin >> n >> m;
for(int i =1;i <= m;i++){
int x,y;
scanf("%lld%lld",&x,&y);
mp[x].push_back(y);
}
// cout<<endl;
int ans=0;
for(int i = 1;i <= n;i++){
memset(dp,0,sizeof dp);
memset(vis,0,sizeof vis);
memset(dep,0,sizeof dep);
dp[i]=0;
vis[i]=1;
dfs(i,0);
for(int j =1;j <= n;j++){
if(dep[j]==2){
int res=1;
// cout<<i<<" "<<j<<" "<<dp[j]<<endl;
for(int k = dp[j];k > dp[j]-2;k--){
res*=k;
}res/=2;
ans+=res;
}
}
}
cout<<ans;
return 0;
}
我还怕这个时间复杂度过不去,开了火车头
。。
总结:
这次失分的原因,是因为T2不细心,遍历的地方写的太快了。下次还是先要把前两题稳拿了,别太赶着做后面两题。
2023.11.10测试 复盘
得分100+100+80,hhh
@2020luke 大佬290 %%%
T1:
怎么那么谁,红题-,好弱智。。
秒了/
#include <cstdio>
#include <cstring>
using namespace std;
char s[103];
int main() {
int _;
scanf("%d", &_);
while(_--) {
scanf("%s", s + 1);
int cntu = 0, cntl = 0, n = strlen(s + 1);
for(int i = 1; i <= n; ++i)
if('A' <= s[i] && s[i] <= 'Z')
++cntu;
else if('a' <= s[i] && s[i] <= 'z')
++cntl;
if(cntu > cntl)
for(int i = 1; i <= n; ++i)
if('a' <= s[i] && s[i] <= 'z')
putchar(s[i] - 32);
else
putchar(s[i]);
else
for(int i = 1; i <= n; ++i)
if('A' <= s[i] && s[i] <= 'Z')
putchar(s[i] + 32);
else
putchar(s[i]);
putchar('\n');
}
return 0;
}
T2:
也很水,看完题面一秒就会了,大概是红~橙。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int a[N],n;
int sum;
int qzh[N];
int hzh[N];
map<int,int> mp;
signed main(){
// freopen("std.in","r",stdin);
cin >> n;
for(int i = 1;i <= n;i++)scanf("%lld",&a[i]);
for(int i =1;i<=n;++i){
sum+=a[i];
qzh[i]=qzh[i-1]+a[i];
}for(int i = n ;i >= 1;i--){
hzh[i]=hzh[i+1]+a[i];
mp[hzh[i]]++;
}
int ans=0;
mp[hzh[1]]--;
for(int i =1;i<=n-2;i++){
mp[hzh[i+1]]--;
if(qzh[i]*3==sum){
ans+=mp[qzh[i]];
}
}
cout<<ans;
return 0;
}
T3:
考试时也不知道是神经组织发什么神经,看错题了,看了半个小时,不是DP,就是假结论贪心,最后5分钟发现读错题目了,火速写暴力。。
正解:(其实也很水)。大概难度在橙。
#include <bits/stdc++.h>
using namespace std;
const int N = 1E5+5;
int n,a[N];
pair<int,int> maxn1[N],minn1[N],maxn2[N],minn2[N];
int main(){
cin>>n;
if(n<3){
cout<<0;
return 0;
}
for(int i = 1;i <= n;i++)scanf("%d",&a[i]);
for(int i = 0;i <= n+1;i++)minn1[i].first=minn2[i].first=1e9;
for(int i = 0;i <= n+1;i++)maxn1[i].first=maxn2[i].first=-1e9;
for(int i = 1;i <= n;i++){
if(maxn1[i-1].first<a[i])maxn1[i].first=a[i],maxn1[i].second=i;
else maxn1[i]=maxn1[i-1];
if(minn1[i-1].first>a[i])minn1[i].first=a[i],minn1[i].second=i;
else minn1[i]=minn1[i-1];
}for(int i = n;i >= 1;i--){
if(maxn2[i+1].first<a[i])maxn2[i].first=a[i],maxn2[i].second=i;
else maxn2[i]=maxn2[i+1];
if(minn2[i+1].first>a[i])minn2[i].first=a[i],minn2[i].second=i;
else minn2[i]=minn2[i+1];
}
for(int i =1;i <= n;i++){
if(minn1[i-1].first < a[i] && a[i] > minn2[i+1].first){cout<<"3\n"<<minn1[i-1].second<<" "<<i<<" "<<minn2[i+1].second;return 0;}
if(maxn1[i-1].first > a[i] && a[i] < maxn2[i+1].first){cout<<"3\n"<<maxn1[i-1].second<<" "<<i<<" "<<maxn2[i+1].second;return 0;}
}cout<<0;
return 0;
}
总结:
其实很简单的题目,一个半小时也能没AK。主要还是读题不仔细,不够细心,下次一定要细心,认真。。
题目来源 AcWing
本文来自博客园,作者:gsczl71,转载请注明原文链接:https://www.cnblogs.com/gsczl71/p/17854616.html
gsczl71 AK IOI!RP = INF 2024年拿下七级勾!