[蓝桥杯 2023] 国B 子2023/双子数 JAVA语言实现

题目

[蓝桥杯 2023 国 B] 子 2023 / 双子数

题目描述

A 子 2023

小蓝在黑板上连续写下从 1 1 1 2023 2023 2023 之间所有的整数,得到了一个数字序列:

S = 12345678910111213 ⋯ 20222023 S = 12345678910111213\cdots 20222023 S=1234567891011121320222023

小蓝想知道 S S S 中有多少种子序列恰好等于 2023 2023 2023

提示,以下是 3 3 3 种满足条件的子序列(用中括号标识出的数字是子序列包含的数字):

1 [ 2 ] 34567891 [ 0 ] 111 [ 2 ] 1 [ 3 ] 14151617181920212223 ⋯ 1[\textbf2]34567891[\textbf0]111[\textbf2]1[\textbf3]14151617181920212223 \cdots 1[2]34567891[0]111[2]1[3]14151617181920212223

1 [ 2 ] 34567891 [ 0 ] 111 [ 2 ] 131415161718192021222 [ 3 ] ⋯ 1[\textbf2]34567891[\textbf0]111[\textbf2]131415161718192021222[\textbf3] \cdots 1[2]34567891[0]111[2]131415161718192021222[3]

1 [ 2 ] 34567891 [ 0 ] 111213141516171819 [ 2 ] 021222 [ 3 ] ⋯ 1[\textbf2]34567891[\textbf0]111213141516171819[\textbf2]021222[\textbf3] \cdots 1[2]34567891[0]111213141516171819[2]021222[3]

注意以下是不满足条件的子序列,虽然包含了 2 2 2 0 0 0 2 2 2 3 3 3 四个数字,但是顺序不对:

1 [ 2 ] 345678910111 [ 2 ] 131415161718192 [ 0 ] 21222 [ 3 ] ⋯ 1[\textbf2]345678910111[\textbf2]131415161718192[\textbf0]21222[\textbf3] \cdots 1[2]345678910111[2]131415161718192[0]21222[3]

B 双子数

若一个正整数 x x x 可以被表示为 p 2 × q 2 p^2 \times q^2 p2×q2,其中 p p p q q q 为质数且 p ≠ q p \neq q p=q,则 x x x
一个 “双子数”。请计算区间 [ 2333 , 23333333333333 ] [2333, 23333333333333] [2333,23333333333333] 内有多少个 “双子数”?

输入格式

输入一个大写字母,表示第几个问题。

输出格式

根据所输入的问题编号,输出对应问题的答案。

提示

答题模板,可供参考。

#include<iostream>
using namespace std;
int main() {
    string ans [] = {
        "The answer of task A", // 双引号中替换为 A 题的答案
        "The answer of task B", // 双引号中替换为 B 题的答案
    };
    char T;
    cin >> T;
    cout << ans[T - 'A'] << endl;
    return 0;
}

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 A、B 题。

子 2023

由题目描述可知,我们需要在一段序列中找到子序列,子序列满足内容完全匹配位置不同两个条件。
题目中让我们查找的为2023的序列。
我们首先简化一下问题,在序列中查找2的序列,2只为一个单一的字符。因此我们只需要从头到尾遍历序列,找到2,结果加一即可,遍历结束,为最终的结果。
接下来,难度加大,在序列中查找20的序列,这时,我们开始遍历整个序列,当遍历序列中2的时候,这不是我们要的结果,当遍历到0的时候,那是不是前面有多少个2,我们到目前为止的子序列中有多少个20
同时,由于我们在遍历序列的时候,是从头到尾遍历一遍,没有回退的操作。因此我们每遍历到一个0,这个0的位置都是唯一的,那么这个0和之前全部的2组成的20序列都是唯一的,不会和前面的结果重复。
因此,我们需要两个变量countTwo和ans。countTwo在遍历序列过程中,遇到2就加一,记录了到当前子序列为止,一共有多少个2。ans则是遇到0时,ans加上countTwo,记录了到目前为止,有多少个20序列。当遍历完毕,ans即为最终的结果。
那回到题目,当序列为2023时,思路与上述的思路完全相同。此时注意序列中存在两个2,因此当遇到2时,序列2的数量加一,同时,序列202的结果加上序列20的数量。
因此,这道题的核心代码为

		/*
			dp[0]代表序列2的数量
			dp[1]代表序列20的数量
			dp[2]代表序列202的数量
			dp[3]代表序列2023的数量
		*/
		if (str.charAt(i) == '2') {
			dp[0]++;
			dp[2] += dp[1];
		} else if (str.charAt(i) == '0') {
			dp[1] += dp[0];
		} else if (str.charAt(i) == '3') {
			dp[3] += dp[2];
		}

完整代码

public static long taskA() {
		long[] dp = new long[4];
		// 构造字符串
		StringBuilder str = new StringBuilder();
		for (int i = 1; i <= 2023; i++) {
			str.append(i);
		}
		// 遍历字符串
		for (int i = 0; i < str.length(); i++) {
			if (str.charAt(i) == '2') {
				dp[0]++;
				dp[2] += dp[1];
			} else if (str.charAt(i) == '0') {
				dp[1] += dp[0];
			} else if (str.charAt(i) == '3') {
				dp[3] += dp[2];
			}
		}
		return dp[3];
	}

B 双子数

这道题求特定组合的数,满足的数之间也看不出规律。因此本题需要暴力求解,找到区间内的素数,然后暴力枚举 出满足条件的数。
那接下来求满足区间的素数。首先由于 x x x 可以被表示为 p 2 × q 2 p^2 \times q^2 p2×q2,x属于区间 [ 2333 , 23333333333333 ] [2333, 23333333333333] [2333,23333333333333] p 2 p^2 p2 q 2 q^2 q2都不超过23333333333333,因此 p p p q q q都不超过 23333333333333 \sqrt {23333333333333} 23333333333333 ,即小于 5 × 1 0 6 5\times10^6 5×106
求素数的方法基本分为四种:

  • 素数判断
  • 素数筛法
  • 埃氏筛
  • 欧拉筛
    因此,本题使用欧拉筛,找到 1 0 7 10^7 107内全部质数。

关键代码

	// 欧拉筛
	public static List<Integer> isPrime() {
		int[] arr = new int[10000010];
		arr[0] = 1;
		arr[1] = 1;
		List<Integer> ret = new ArrayList<>();
		for (int i = 2; i < 10000010 - 1; i++) {
			if (arr[i] == 0) {
				ret.add(i);
			}
			for (int j = 0; j < ret.size() && ret.get(j) * i < 10000010; j++) {
				arr[ret.get(j) * i] = 1;
				if (i % ret.get(j) == 0)
					break;
			}
		}
		return ret;
	}

找到质数之后,进行枚举,记录满足条件的全部个数。
此时注意,如果使用JAVA的long类型或者C++的long long计算 p 2 × q 2 p^2 \times q^2 p2×q2,存在部分结果溢出。导致最终结果不对,C++可以选择使用__int128,而JAVA我采用大整数类BigInteger来计算结果。

关键代码

		long ans = 0;
		List<Integer> primeArr = isPrime();
		for (int i = 0; i < primeArr.size(); i++) {
			for (int j = i + 1; j < primeArr.size(); j++) {
				BigInteger p = new BigInteger(String.valueOf(primeArr.get(i)));
				BigInteger q = new BigInteger(String.valueOf(primeArr.get(j)));
				BigInteger t1 = new BigInteger("2333");
				BigInteger t2 = new BigInteger("23333333333333");
				if (p.multiply(p).multiply(q).multiply(q).compareTo(t1) == -1)
					continue;
				if (p.multiply(p).multiply(q).multiply(q).compareTo(t2) == 1) {
					break;
				}
				ans++;
			}
		}

本题完整代码

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		String ch = scanner.next();
		scanner.close();
		if (ch.equals("A")) {
			System.out.println(taskA());
		} else {
			long ans = 0;
			List<Integer> primeArr = isPrime();
			for (int i = 0; i < primeArr.size(); i++) {
				for (int j = i + 1; j < primeArr.size(); j++) {
					BigInteger p = new BigInteger(String.valueOf(primeArr.get(i)));
					BigInteger q = new BigInteger(String.valueOf(primeArr.get(j)));
					BigInteger t1 = new BigInteger("2333");
					BigInteger t2 = new BigInteger("23333333333333");
					if (p.multiply(p).multiply(q).multiply(q).compareTo(t1) == -1)
						continue;
					if (p.multiply(p).multiply(q).multiply(q).compareTo(t2) == 1) {
						break;
					}
					ans++;
				}
			}
			System.out.println(ans);
		}

	}

	public static long taskA() {
		long[] dp = new long[4];
		// 构造字符串
		StringBuilder str = new StringBuilder();
		for (int i = 1; i <= 2023; i++) {
			str.append(i);
		}
		// 遍历字符串
		for (int i = 0; i < str.length(); i++) {
			if (str.charAt(i) == '2') {
				dp[0]++;
				dp[2] += dp[1];
			} else if (str.charAt(i) == '0') {
				dp[1] += dp[0];
			} else if (str.charAt(i) == '3') {
				dp[3] += dp[2];
			}
		}
		return dp[3];
	}

	// 欧拉筛
	public static List<Integer> isPrime() {
		int[] arr = new int[10000010];
		arr[0] = 1;
		arr[1] = 1;
		List<Integer> ret = new ArrayList<>();
		for (int i = 2; i < 10000010 - 1; i++) {
			if (arr[i] == 0) {
				ret.add(i);
			}
			for (int j = 0; j < ret.size() && ret.get(j) * i < 10000010; j++) {
				arr[ret.get(j) * i] = 1;
				if (i % ret.get(j) == 0)
					break;
			}
		}
		return ret;
	}
}

结尾

代码思想基本上正确,但是放到洛谷上运行,第二个测试用例会超时。
在这里插入图片描述
这是洛谷评测以C++为主,JAVA在运行时间上处于劣势。结合蓝桥杯官方的评测标准
在这里插入图片描述
无伤大雅,不必理会。

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值