2023CSP复赛/NOIP备战模拟赛复盘集合

发布时间 2023-11-24 19:49:36作者: gsczl71

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