2020蓝桥杯第一轮省赛B组题解(附题面)

前言

4号省赛,到今天依旧没有一篇题解,索性自己写一个,当然不是标准答案,纯粹是个人的理解,可能会有错误,欢迎指正。个人认为这次省赛的题质量不错,都是能写的题,难度梯度也不错,最后一题可能偏简单了。
填空答案没保存,因此只写思路解法。
编程题稍后补上代码。

A 跑步训练

类似的问题很多,比如蜗牛爬树,爬井什么的。可以直接算,也可以编程模拟。需要注意的是最后一次跑步的一分钟剩余体力是400而不是100。

B 纪念日

没什么好说的,手算吧。听说excel里有函数可以直接得到天数。

C 合并检测

假设总体为1,合并检测需要消耗1/K盒试剂,其中有0.01的被感染者需要额外花费0.01*K盒试剂,即总共需要花费1/K+K/100盒试剂,根据基本不等式得K取10。

D REPEAT程序

可以写翻译程序,也可以取巧,替换文本改成代码,然后直接跑。
写翻译程序的话可以写一个dfs,每次发现repeat就起一个新的调用,传的参数我是用的缩进,以此得到优先级,缩进较长的需要优先计算得到结果。发现本行缩进小于参数时即可返回到上层。
给一个我写的翻译程序,基于dfs处理嵌套REPEAT

#include <iostream>
#include <cstdio>
using namespace std;

const int maxn = 1000010;
string s[1005];
int i = 0;
int getp(string s)
{
    
    
	int p = 0;
	while(s[p] == ' ')p++;
	return p;
}
int x = 0;
int dfs(int y)
{
    
    
	int p = 0,v = 0;
	while(x<i){
    
    
		x++;
		cout << s[x] << endl << x << ' ' << y << ' ' << v << endl;
		p = getp(s[x]);
		if(p<y){
    
    
			x--;
			return v;
		}
		if(s[x][p] == 'R')
		{
    
    
			v+=(s[x][p+7]-'0')*dfs(p+4);
		}
		else if(s[x][p] == 'A'){
    
    
			v+=s[x][p+8]-'0';
		}

	}
	return v;
}
int main()
{
    
    
    freopen("prog.txt","r",stdin);

    long long ans = 0;
    //getline(cin,s[0]);
    while(!cin.eof()){
    
    
    	getline(cin,s[i]);
    	i++;
    }
    ans = dfs(0);
    cout << ans;
	return 0;
}

E 矩阵

观察后可发现左上与右下只能取1,2020;而与1相邻的两个格子,即一行二列与二行一列,只能取2,3;再往后移一列,即一行三列与二行二列,只能取4,5;推断可得一共有1009这样的组,那么答案就是2^1009mod2020。

F 整除序列

如题意,直接对输入执行循环除2即可。

G 解码

可以采用预读的方式输出,即每次读两个字符,看第二位是不是数字,不是则输出当前位字符,是则输出指定数字个数的当前字符并位置+1;也可以直接顺序输出,每次读到数字时对前一位进行补偿。

H 走方格

简单dp即可,dp[i][j]=dp[i-1][j]+dp[i][j-1]。
也可dfs求解,但当数值接近30时可能会超时,我打了个表。

I 整数拼接

拼接数可以看成由两部分组成,第一部分为a*10^b,第二部分为c,因此我们可以对第一部分进行预处理,然后查询对于每个c有多少个像第一部分这样的数满足两部分之和modK=0,加进ans即可。

具体做法是创建一个二维数组mod,一维表示10的幂次,二维表示modK的余数。计算每个数与10,100,1000…10^9的乘积modK的余数,然后将对应的二维数组的单元+1。处理完后mod[a][b]即表示一个数后面跟了a个0,modK余数为b,这样的数(即前文提到的第一部分)的个数。然后对每个数进行处理,假设这个数为上文提到的拼接数的第二部分,那么符合条件的第一部分modK与这个数modK相加,再对K取模结果应为0。

为了方便处理,我写了两个函数,一个getl,返回传入值的位数;一个getll,返回10的幂次。这样,在对每个数a进行处理时,我们就可以直接访问之前预处理得到的mod数组。一维为getl(a),表示第一部分得跟多少个0,第二部分为(K-a modK)modK,表示两部分之和modK为0。得到的结果即为满足条件的a*10^b的个数。

需要注意的是自己与自己匹配的情况,即第一部分的a与第二部分的c是同一个数。可以通过处理时检查当前数a乘以getll(a)对K取模,与我们需要的余数(K-a modK)modK是否一样。如果一样,表示这个余数的数量中是包含当前处理的这个数的,ans应-1。

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int maxn = 100010;
int mod[10][maxn];
long long a[maxn];
int getl(long long a)
{
    
    
	int r = 0;
	while(a>0)
	{
    
    
		r++;
		a/=10;
	}
	return r;
}
long long getll(int a)
{
    
    
	long long r = 1;
	while(a--)r*=10;
	return r;
}
int main()
{
    
    
	int n,k;
	long long ans = 0;
	cin >> n >> k;
	for(int i = 0;i < n;i++)
	{
    
    
		cin >> a[i];
		long long p = 0,q = 1;
		while(p<10)
		{
    
    
			long long t = (a[i]*q)%k;
			mod[p][t]++;
			p++;
			q*=10;
		}
	}
	//cout << mod[1][0] << ' ' << mod[1][1];
	for(int i = 0;i < n;i++)
	{
    
    
		//cout << getl(a[i]) << ' ' << (k-a[i]%k)%k << endl;
		ans+=mod[getl(a[i])][(k-a[i]%k)%k];
		//cout << a[i]*getll(getl(a[i]))%k;
		if((a[i]*getll(getl(a[i])) + a[i])%k == 0)ans--;
	}
	cout << ans;
	return 0;
	
}

J 网络分析

通过并查集求解,由于只需在最后求得每个点的权重,因此并查集只需要更新每个根值即可。
具体做法是设置一个增量数组,保存每个union集合里点的增量值,每次集合合并时,被合并的集合的根的增量值减去将合并到的集合的根的增量值,这样在最后求点权重时,我们只要自底向上寻根,每次加上增量值即可得到点的最终权重。在合并集合时,只更新根节点的父节点,其他节点的父节点仍为根节点,增量值也只更新根节点即可,可以类比成生成了一棵树,求叶节点权值即是寻根过程。

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int maxn = 10010;
int ans[maxn];
int add[maxn];
int fa[maxn];
int v[maxn];
int f(int i)
{
    
    
	if(fa[i]!=i)return f(fa[i]);
}
void uni(int a,int b)
{
    
    
	if(f(a) == f(b)) return;
	else
	{
    
    
		a = f(a),b = f(b);
		if(v[a]>=v[b])
		{
    
    
			add[b]-=add[a];
			fa[b] = a;
			v[a]+=v[b];
		}
		else
		{
    
    
			add[a]-=add[b];
			fa[a] = b;
			v[b]+=v[a];
		}
	}
}
int main()
{
    
    
	int n,m;
	int c = n;
	cin >> n >> m;
	for(int i = 1;i <= n;i++){
    
    
		fa[i] = i;v[i] = 1;
	}
 	for(int i = 0;i < m;i++)
	{
    
    
		int a,b,c;
		cin >> a >> b >> c;
		if(a == 1)
		{
    
    
			uni(b,c);
		}
		else
		{
    
    
			add[f(b)]+=c;
		}
	}
	for(int i = 1;i <= n;i++)
	{
    
    
		int t = i;
		ans[i]+=add[i];
		while(fa[t]!=t)
		{
    
    
			ans[i]+=add[fa[t]];
			t=fa[t];
		}
	}
	for(int i = 1;i <= n;i++)
	cout << ans[i] << endl;
	return 0;
}

题面链接:https://pan.baidu.com/s/1xcNT6pqKltR_WLhlep64OA
提取码:5789
挂了私信我拿。

猜你喜欢

转载自blog.csdn.net/qq_39586345/article/details/107174706
今日推荐