CSP-J复赛模拟4赛后补题报告

CSP-J复赛模拟4赛后补题报告

日期:2023年10月4日星期二

                                                                  学号:S08683

                                                                  姓名:王皓轩

1. 比赛概况

比赛总分共 4 题,满分 400,赛时拿到 50 分,其中第一题0分,第二题50分,第三题0分,第四题0分。

2. 比赛过程:

先做的第一题,在中间套了两个分离的,样例过了,但是报0了。然后做第二题用二维数组存两种数据+前缀和,但数据类型开错了。第三题替换顺序看不懂。第四题写了写深搜,但样例也不对,没有思路。

3. 题解报告:

(1) 第一题:复读机

情况:赛中0分,已补题

题意:输入t组数据,每组一个n,一个n个字符的字符串,包含数字和字母,进行复读操作后输出。例如:kdy3,表示将kdy复读3遍,输出为:kdykdykdy

a5b2为aaaaabaaaaab,z11y2为zzzzzzzzzzzyzzzzzzzzzzzy

赛时本题做题想法:直接用一个string把字符串输入,然后对数字和字母分开统计,将遇到数字统计后,把前方的所有字符串通过循环放入。

题解:想法与题解是一样的,但是在外面控制字符串变量时,和内层循环变量一样,导致爆0.

AC 代码:

#include<bits/stdc++.h>
using namespace std;
int t,n,pos,tmp;
string a,c,ans;
int main()
{
	cin>>t;
	while(t--){
		cin>>n>>a;
		a=" "+a,ans="";
		for(int i=1;i<=n;i++){
			if(a[i]>='a'&&a[i]<='z')ans+=a[i];
			else{
				tmp=0,pos=i;
				while(a[pos]>='0'&&a[pos]<='9'){
					tmp*=10;
					tmp+=a[pos]-'0';
					pos++;
				}
				i=pos-1;
				c=ans,tmp--;
				while(tmp--)ans+=c;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

(2) 第二题:小可的矛与盾

情况:赛中50分,已补题。

题意:n个人站成一排,为‘1’,‘0’,1代表此人是盾,0代表此人是矛,盾的防御和矛的攻击力ai=i,求出1个pos,把队伍分成两个阵营,1~pos只算矛的攻击总和,pos+1~n只算盾防御总和,求怎样让两个总和相差最少。

样例:输入:7 1000101 输出:2

赛时本题做题想法:开一个a[n][2]的数组,行代表第几个,列0代表到这个的攻击总和,列代表防御总和,最后用总的防御-当前防御和当前攻击做差,求最小值。时间复杂度为O(n)。但又因为数字太大,需要开long long。我没开直接扣了50分;

题解:有两种方法

     一种是递推打表存,一种是前缀和。

     递推只需存对应矛的攻击力,对应盾累加防御力和对应自身防御力,后面求就好了。

     前缀和也可以分别开两个数组来存,一个存防御,一个存攻击。

AC 代码:

#include<bits/stdc++.h>
using namespace std;
long long n,cnt1,cnt2;
long long cnt[100005][2];
int main(){
	//freopen("spearshield.in","r",stdin);
	//freopen("spearshield.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		char x;
		cin>>x;
		if(x-'0'==0){
			cnt1+=i;
		}
		if(x-'0'==1){
			cnt2+=i;
		}
		cnt[i][0]=cnt1;
		cnt[i][1]=cnt2;
	}
	long long pos=1e9+5;
	for(int i=1;i<=n;i++){
		int x=cnt[n][1]-cnt[i-1][1],y=cnt[i-1][0];
		if(abs(x-y)<pos){
			pos=abs(x-y);
		}
	}
	cout<<pos;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

(3) 第三题:不合法字符串

情况:赛中0分,已补题。

题意:有t组数据,输入n,n个非法字符串和一篇“小说”。

如果在小说中出现了非法字符串,请用“*”和谐。

样例:输入:1 1 bbcbb bbcbbbcbbb    输出:bbcb*bcbbb

赛时本题做题想法:从前往后找子串,然后从左往右替换*

题解:应该是找到子串后,改最后一位为最优。例如“bbcbb”为非法,而小说为“bbcbbbcbbb”如果改1~4的一个,那么在5~9还是能找,但删除第五个,那么就都没了

AC 代码:

#include<bits/stdc++.h>
using namespace std;
int t,n,m;
string s[100000],tgt;
int main()
{
	cin>>t;
	while(t--){
		cin>>m;
		for(int i=1;i<=m;i++){
			cin>>s[i];
		}
		cin>>tgt;
		n=tgt.length();
		tgt=' '+tgt;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(i<s[j].length())continue;
				if(tgt.substr(i-s[j].length()+1,s[j].length())==s[j]){
					tgt[i]='*';
				}
			}
		}
		for(int i=1;i<=n;i++){
			cout<<tgt[i];
		}
		cout<<endl;
	}
	return 0;
}

(4) 第四题:虚假的珂朵莉树

情况:赛中0分,已补题。

题意:一棵树,根节点为1的树,都有权值。与根节点的距离为这个节点的深度,小可会在这棵树上加m条虚假边(任意一条虚假边不会和原来的树边或其他虚假边重合(增加的虚假边不影响节点深度))。之后又有q次操作,有两种,操作2结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 小的结点重复操作2。

操作2:结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 大的结点重复操作2。

赛时本题做题想法:看上去就很蒙,然后想用一个邻接矩阵存关系然后用深搜对k的两种操作进行加,但显然会时间超限并且存不下。

题解:每次将传递过来的操作以和当前合并,所以统计只需两个桶数组,通过反转数组用一个dfs求出节点深度,用数组累计权值,前缀和辅助最后相加求和。数据范围过大需要通过前向星来储存树。

AC 代码:

#include<bits/stdc++.h>
#define pr pair<int,int>
#define mk make_pair
using namespace std;
const long long p=1e9+7;
struct node{
 int to,next;
}e[5000005];
vector<pr> g;
long long a[1000005],up[1000005],down[1000005];
int n,m,q,cnt,head[1000005],d[1000005];
void add(int u,int v){
    e[++cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
void dfs(int u,int fa){
	g.push_back(mk(d[u],u));
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v==fa) continue;
		d[v]=d[u]+1;
		dfs(v,u);
	}
}
int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	dfs(1,1);
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=q;i++){
		int pd,u,v;
		cin>>pd>>u>>v;
		if(pd==1) up[u]=(up[u]+v)%p;
		else down[u]=(down[u]+v)%p;
	}
	sort(g.begin(),g.end());
	for(int i=0;i<g.size();i++){
		int x=g[i].second;
		for(int j=head[x];j;j=e[j].next){
			int y=e[j].to;
			if(d[y]>d[x]) down[y]=(down[y]+down[x])%p;
		}
	}
	reverse(g.begin(),g.end());
	for(int i=0;i<g.size();i++){
		int x=g[i].second;
		for(int j=head[x];j;j=e[j].next){
			int y=e[j].to;
				if(d[y]<d[x]) up[y]=(up[y]+up[x])%p;
		}
	}
	for(int i=1;i<=n;i++) cout<<(a[i]+up[i]+down[i])%p<<" ";
	return 0;
}

4 赛后总结:

本次比赛中总体还是比昨天简单一些,但是循环变量套错了,数据范围也没有注意好,之后一定要看好变量和数据范围!!!而且在最后一题里虽然满分还是不可能,链式前向星也学过了暴力也能拿一点分,所以复习还是要尽量全面一些!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值