CSP-J 第二轮 模拟赛五补题报告

日期:2023年10月5日

学号:S07246

姓名:江守栋

目录

·  比赛概况

· 比赛过程

· 题目分析

T1 【重复判断  repeat】

1、题目大意

2、比赛中的思考

3、解题思路

4、AC代码

T2【歪果仁学乘法  multiplication】

1、题目大意

2、比赛中的思考

3、解题思路

4、AC代码

T3【去重求和  summation】

1、题目大意

2、比赛中的思考

3、解题思路

4、AC代码

T4【点集操作  point】

1、题目大意

2、比赛中的思考

3、解题思路

4、AC代码


·  比赛概况

总分 :  230 / 400

T1T2T3T4
ACAC0分30分

今天的 T4 我的思路是正确的,但是数据量炸掉了,扣了 70 分

· 比赛过程

前两题水题,15分钟秒了

看了眼第三题,感觉需要推很久,就先没做

第四题是一道图论,昨天晚上做了很久邻接表的题,现在掌握得比较熟练了

赛时在纸上画了一堆图,推了很久,找到了思路,

可惜我写了两层循环,在平台里提交过了,但是判题机运行时间超限了,丢了 70 分 。。。

· 题目分析

T1 【重复判断  repeat】

1、题目大意

判断字符串a是否是字符串b重复若干次得到的。

2、比赛中的思考

是道水题,模拟秒了

3、解题思路

两个指针同时分别遍历字符串 a 和 字符串 b ,

因为 b 的长度短,可能会多次遍历,所以需要每次 ++ 后 % b.size(),保证下标不会越界

如果 a_{i _{}} != b_{i} 则标记为 false ,如果遍历结束了,而 j !=0 说明 a 中出现了多余元素,不是重复字符串。

4、AC代码

#include<bits/stdc++.h>
using namespace std;
int T;
string s,s1;
int main()
{
	freopen("repeat.in","r",stdin);
	freopen("repeat.out","w",stdout);
	cin>>T;
	while(T--){
		bool flag=1;
		cin>>s>>s1;
		int j=0;
		for(int i=0;i<s.size();i++){
			if(s[i]!=s1[j]){
				flag=0;
				break;
			}
			j++;
			j%=s1.size();
		}
		if(flag==1&&j==0){
			cout<<"YES";
		}
		else{
			cout<<"NO";
		}
		cout<<endl;
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2【歪果仁学乘法  multiplication】

1、题目大意

现有一种不需要九九乘法表也可以计算乘法的方式,对于a × b:

  1. 将a,b的每一位上的数码画成线,不同位之间分隔开。
  2. a 和 b 的方向垂直画出。
  3. 数出每个方向上交点的个数,即是 c 对应位置上的数码。

样例图给出了计算12 ×13的方法:

1.红色线分别画出 1 条和 2 条;
2.蓝色线分别画出 1 条和 3 条;
3.数出红、蓝色线的交点个数,依次为 1,5,6 个;
4.得到答案:12 × 13 = 156。

给出两个数字 a, b,求它们的乘积时交点的总个数是多少

2、比赛中的思考

仍然是一道水题,俩循环秒了

3、解题思路

根据图例即可发现答案等于两个因数按位相乘后相加,注意不要理解成将乘积数位分离,

这样会漏掉计算中进位的交叉点。

只需两个 while 将 a 和 b 数位分离并相加,最后将得到的两个值相乘即可

4、AC代码

#include<bits/stdc++.h>
using namespace std;
int a,b;
int main()
{
	freopen("multiplication.in","r",stdin);
	freopen("multiplication.out","w",stdout);
	cin>>a>>b;
	int sum1=0,sum2=0;
	while(a!=0){
		sum1+=a%10;
		a/=10;
	}
	while(b!=0){
		sum2+=b%10;
		b/=10;
	}
	cout<<sum1*sum2;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3【去重求和  summation】

1、题目大意

定义sum(l,r) 为 a_{l} ~  a_{r} 间的数字去重后的和。

\sum _{l=1}^{n} \sum_{r=1}^{n}  sum(l,r)

2、比赛中的思考

 不会,而且第四题耗费太多时间,这道题没有思路,只能输出样例

3、解题思路

只计算每个数第一次出现时对答案的贡献

如果第 i 个元素前面(以左端点 l 为起点)没有重复的,则它在当前以 l 为左端点的所有 sum 中出现次数为 n-i+1

如果第 i 个元素前面(以左端点 l 为起点)没有重复的,则它在当前以 l 为左端点的所有 sum 中出现次数为 0

第 i 个元素在所有的 sum 中最多出现 i 次

如果第 i 个元素前面与 a_{i} 相同的元素的编号为 x ,则第 i  个元素出现次数为 i-x 

综上所述,可以得出,第 i 个元素的对答案的贡献为:

ans+=(n-i+1) * a[i] *(i-x)

4、AC代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
ll a[N], n;
ll ans = 0;
map<ll, ll> p;
int main() {
    freopen("summation.in", "r", stdin);
    freopen("summation.out", "w", stdout);
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]);
    for (ll i = 1; i <= n; i++) {
    	ll x = p[a[i]];
    	ans = (ans + (n - i + 1)  % mod * a[i]  % mod * (i - x) % mod) % mod;   //公式
    	p[a[i]] = i;
	}
	printf("%lld", ans % mod);
    fclose(stdin);
    fclose(stdout);
	return 0;
}

T4【点集操作  point】

1、题目大意

一个有向无环图,对这个图做任意次 modify(i,j) 操作后图中剩余的最小点数

其中 modify(i,j)  的含义为:

1. 任选不同的两个点
2. 称 A_{i} 为 i 能到达的所有点组成的点集, A_{j} 为 j 能到达的所有点组成的点集 。(注意:每个点可以到达的点集包含这个点本身)
3. 设 B 为一个最大的点集,满足 B 既是 A_{i} 的子集,又是 A_{j} 的子集 。
4. 将 B 在图中变成一个新点,B 内的所有边全部删除。点集 B 以外的点与点集 B 以内的点的连边关系转移到新点上。

2、比赛中的思考

根据昨天的比赛找到了自己的短板,昨天回家专门练了很久图论的题,也算是临阵磨枪了

我的思路是先用链式前向星存图,再输入时统计 x 与 y 的出度和入度,输入结束后开始暴搜

首先将 ans 初始值赋为 n ,初始默认不需要删除任何点,随后开始遍历每个结点,

如果该结点既有入度也有出度,则肯定要将这个结点与它的邻接点删除,循环将结点标记为删除

并将 ans-- 意为删除掉了一个点,最后输出 ans 即可 , 赛后在平台上提交了,显示AC

但是判题机狠狠地给了我一巴掌 , 跑出来之后时间超限,只得了30分

#include<bits/stdc++.h>
using namespace std;
int n,m,ver[2000005],head[2000005],nxt[2000005],tot=0;
bool vis[2000005];
int sum[2000005][5];
void add(int x,int y){
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int main(){
	freopen("point.in","r",stdin);
	freopen("point.out","w",stdout);
	memset(head,-1,sizeof head);
	memset(vis,0,sizeof vis);
	cin>>n>>m;
	for(long long i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		add(x,y);
		sum[x][2]++;
		sum[y][1]++;
	}
	long long ans=n;
	for(long long i=1;i<=n;i++){
		if(vis[i]||(sum[i][1]>=1&&sum[i][2]>=1)){
			for(long long j=head[i]; j!=-1;j=nxt[j]){
				if(vis[ver[j]]==0){
					ans--;
				}
				vis[ver[j]]=1;
			}
		}
	}
	cout<<ans;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

呜呜呜呜呜呜呜呜呜呜呜我的70分。。。

3、解题思路

首先将 ans 置为 0

前向星存图,但是在输入时需要使用数组存储  x[i]  和 y[i] ,而且只需要统计入度。

输出结束后判断一下 x[i] 的入度 , 如果该节点的入度为零,则一定不需要删除,因为没有结点能够到达它,将其放入数组v中,并将 ans++

随后遍历所有的边,如果这条边的起点有入度,那么这条边指向的结点就能与起点一起合并

将这条边指向的点标记为合并

然后遍历数组 v ,需要用两层循环,外层遍历 v ,内层沿着以 i 为起点的链表遍历,如果遍历到的点的对应点入度为零,将这个点保留或把这个点下面的所属点合并成一个点,ans++

最后输出 ans

4、AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n,m,x[MAXN*2],y[MAXN*2],in[MAXN];
int head[MAXN],num,ans;
bool b[MAXN],vis[MAXN];
vector<int>v;
struct node{
	int to,next;
}e[MAXN*2];
void add(int u,int v){
	e[++num].to=v; 
	e[num].next=head[u];
	head[u]=num;
}
int main(){
	cin >> n >> m;
	for(int i=1;i<=m;++i){ 
		cin >> x[i] >> y[i];
		add(x[i],y[i]);
		in[y[i]]++;
	}
	for(int i=1;i<=n;++i){
		if(!in[i]){
			v.push_back(i);
			ans++;
		}
	}
	for(int i=1;i<=m;++i){
		if(in[x[i]])b[y[i]]=1;
	}
	for(int i=0;i<v.size();++i){
		for(int j=head[v[i]];j;j=e[j].next){
			if(!b[e[j].to]){
				b[e[j].to]=1;
				ans++;
			}
		}
	}
	cout << ans;
	return 0;
}____________________

·  赛后总结

        今天的难度可以说是这五天中最简单的,前两道题也是一上来就直接秒掉了,但是第四题的图论卡了我很久,即使想了很久,最后扣掉了70分(死因:两层循环时间超限),300分直接变成270,以后做题要看准数据范围,对于可能超限的循环或变量内存,不要马马虎虎的提交上去,要想到优化的策略

        第三题这种偏向数学思维的题目应该列表画图,找到其中的规律,只要把思路打开,想清楚题目,代码往往是非常好写的,以后也要注重于这方面的练习

        比赛加油!rp++ ! ! !

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值