有 a 条红丝带、b 条蓝丝带、c 条黄丝带,每条丝带都有确定的长度。 每种颜色各选一条丝带,三条丝带组成一组。 要把这一组的三条丝带全部剪成长度相同的小段,并且不能有剩余。 小段的长度是整数。 能这样剪的小段长度中最长的那个,叫做这一组的小段长度。 长度相同的不同丝带也算作不同的丝带。 所以能组成的组一共有 a × b × c 种。 第一行给出 a、b、c。 第二行给出 a 条红丝带的长度,第三行给出 b 条蓝丝带的长度,第四行给出 c 条黄丝带的长度。 第五行给出询问的个数 q,接下来 q 行每行给出一个询问 g。 对每个询问,输出小段长度恰好是 g 的组有多少种。
输入
1 ≤ a, b, c ≤ 9000。 丝带的长度是 1 到 9000 的整数。 1 ≤ q ≤ 1000。 1 ≤ g ≤ 9000。 同一行的数用一个空格隔开给出
输出
每个询问一行,输出组的个数(不带逗号,只写数字)
样例 1 · stdin
4 2 3
6 12 9 6
4 6
12 18 8
5
1
2
3
6
4
stdout
4
10
2
6
2
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
一组的小段长度就是三个长度的最大公约数。 一组一组地看,最多有 9000 × 9000 × 9000 = 7290 亿种,算不完。 换个办法:用素数筛里划掉倍数时的那种循环,也就是只跳着看 d、2d、3d、… 的循环,对每个 d 按颜色数出“长度是 d 的倍数的丝带有几条”。 d 从 1 到 9000 全做一遍也只是 9000 × (1 + 1/2 + 1/3 + …),每种颜色约 9 万次。 三个数相乘就是“三个长度都是 d 的倍数的组”的个数,也就是最大公约数是 d、2d、3d、… 之一的组的个数。 从中减去最大公约数是 2d、3d、… 的组的个数,剩下的就是恰好为 d 的组。 所以要从大的 d 开始填。 Python 用切片的步长只取倍数,如 sum(count[d::d]);C、C++、Java、C#、JS、TS 用 for (m = d; m <= LIMIT; m += d) 跳着走,Craykon 用 循环 (m = d; m <= limit; m += d)。 最大的答案是 9000 × 9000 × 9000 = 729000000000,所以 C、C++ 用 long long,Java、C# 用 long 来存。 JS、TS 的 number 和 Craykon 的数在 2^53 以内是精确的,直接用。 陷阱一:漏掉减法,答的就是“最大公约数是 d 的倍数的组”。 示例的 g = 2 会得到 18 而不是 10。 陷阱二:从小的 d 开始填,该减的 2d、3d、… 的答案还是 0,什么也没减掉。 示例里同样得到 18。 陷阱三:长度相同的丝带也是不同的丝带。 只数长度的种类,示例的 g = 2 会变成 6(红色里有两条长度为 6 的)。 陷阱四:在 Java 里把三个数按 int 相乘,乘积超过 2^31 时溢出后的值才放进 long。 示例的数太小,看不出来。 陷阱五:在 Craykon 里一组一组地数,遇到大的输入会带着“耗时太长”的提示停止。 先把长度相同的丝带合并、再两种颜色两种颜色地数最大公约数的做法(最大公约数最多要算 1.6 亿次),用 C 实测也要约 4 秒,超过了 2 秒的时间限制。 另一方面,不跳着走、对每个 d 把 1 到 9000 全看一遍、只挑出 m % d == 0 的做法(约 8 千万次),用 C 只要 0.1 秒。 同样的做法用 Python 要约 2 秒,在更慢的评测机上可能超时,Craykon 则会因“耗时太长”而停止。 所以跳着走的循环只有在慢的语言里才非用不可,而无论哪种语言都少不了的,是按倍数来数、再从大的一端往下减的想法。
Pythonmain.py · 41 行
import sysLIMIT = 9000def multiples_table(lengths): """table[d] = 길이가 d의 배수인 리본의 수""" count = [0] * (LIMIT + 1) for x in lengths: count[x] += 1 table = [0] * (LIMIT + 1) for d in range(1, LIMIT + 1): # 체처럼 d의 배수만 건너뛰며 본다: 모두 합쳐 LIMIT × (1 + 1/2 + 1/3 + …) 번 table[d] = sum(count[d::d]) return tabledef main(): data = sys.stdin.read().split() a, b, c = int(data[0]), int(data[1]), int(data[2]) pos = 3 red = multiples_table(map(int, data[pos:pos + a])) pos += a blue = multiples_table(map(int, data[pos:pos + b])) pos += b yellow = multiples_table(map(int, data[pos:pos + c])) pos += c # exact[d] = 가장 긴 도막이 딱 d인 묶음의 수 exact = [0] * (LIMIT + 1) for d in range(LIMIT, 0, -1): # 큰 d부터: 2d, 3d, …의 답이 먼저 정해져 있어야 한다 total = red[d] * blue[d] * yellow[d] # 세 길이가 모두 d의 배수인 묶음 total -= sum(exact[2 * d::d]) # 그 가운데 최대공약수가 2d, 3d, …인 묶음을 뺀다 exact[d] = total q = int(data[pos]) out = [str(exact[int(g)]) for g in data[pos + 1:pos + 1 + q]] print("\n".join(out))main()
JavaScriptmain.js · 32 行
const data = require("fs").readFileSync(0, "utf8").split(/\s+/).filter(Boolean).map(Number);const LIMIT = 9000;let pos = 0;const a = data[pos++], b = data[pos++], c = data[pos++];// table[d] = 길이가 d의 배수인 리본의 수function multiplesTable(n) { const count = new Array(LIMIT + 1).fill(0); for (let i = 0; i < n; i++) count[data[pos++]]++; const table = new Array(LIMIT + 1).fill(0); for (let d = 1; d <= LIMIT; d++) { for (let m = d; m <= LIMIT; m += d) table[d] += count[m]; // d의 배수만 건너뛰며 본다 } return table;}const red = multiplesTable(a);const blue = multiplesTable(b);const yellow = multiplesTable(c);// exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 2^53보다 작아 number로 정확하다const exact = new Array(LIMIT + 1).fill(0);for (let d = LIMIT; d >= 1; d--) { // 큰 d부터 let total = red[d] * blue[d] * yellow[d]; for (let m = 2 * d; m <= LIMIT; m += d) total -= exact[m]; exact[d] = total;}const q = data[pos++];const out = [];for (let i = 0; i < q; i++) out.push(exact[data[pos++]]);console.log(out.join("\n"));
TypeScriptmain.ts · 33 行
import { readFileSync } from "node:fs";const data: number[] = readFileSync(0, "utf8").split(/\s+/).filter(Boolean).map(Number);const LIMIT = 9000;let pos = 0;const a = data[pos++], b = data[pos++], c = data[pos++];// table[d] = 길이가 d의 배수인 리본의 수function multiplesTable(n: number): number[] { const count: number[] = new Array<number>(LIMIT + 1).fill(0); for (let i = 0; i < n; i++) count[data[pos++]]++; const table: number[] = new Array<number>(LIMIT + 1).fill(0); for (let d = 1; d <= LIMIT; d++) { for (let m = d; m <= LIMIT; m += d) table[d] += count[m]; // d의 배수만 건너뛰며 본다 } return table;}const red = multiplesTable(a);const blue = multiplesTable(b);const yellow = multiplesTable(c);// exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 2^53보다 작아 number로 정확하다const exact: number[] = new Array<number>(LIMIT + 1).fill(0);for (let d = LIMIT; d >= 1; d--) { // 큰 d부터 let total = red[d] * blue[d] * yellow[d]; for (let m = 2 * d; m <= LIMIT; m += d) total -= exact[m]; exact[d] = total;}const q = data[pos++];const out: number[] = [];for (let i = 0; i < q; i++) out.push(exact[data[pos++]]);console.log(out.join("\n"));
JavaMain.java · 44 行
import java.io.*;import java.util.*;public class Main { static final int LIMIT = 9000; static StreamTokenizer in; static int next() throws IOException { in.nextToken(); return (int) in.nval; } // table[d] = 길이가 d의 배수인 리본의 수 static long[] multiplesTable(int n) throws IOException { int[] count = new int[LIMIT + 1]; for (int i = 0; i < n; i++) count[next()]++; long[] table = new long[LIMIT + 1]; for (int d = 1; d <= LIMIT; d++) { for (int m = d; m <= LIMIT; m += d) table[d] += count[m]; // d의 배수만 건너뛰며 본다 } return table; } public static void main(String[] args) throws IOException { in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in))); int a = next(), b = next(), c = next(); long[] red = multiplesTable(a); long[] blue = multiplesTable(b); long[] yellow = multiplesTable(c); // exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 int를 넘으므로 long long[] exact = new long[LIMIT + 1]; for (int d = LIMIT; d >= 1; d--) { // 큰 d부터 long total = red[d] * blue[d] * yellow[d]; for (int m = 2 * d; m <= LIMIT; m += d) total -= exact[m]; exact[d] = total; } int q = next(); StringBuilder out = new StringBuilder(); for (int i = 0; i < q; i++) out.append(exact[next()]).append('\n'); System.out.print(out); }}
C#Main.cs · 45 行
using System;using System.Text;class Program{ const int Limit = 9000; static string[] tokens; static int pos = 0; // table[d] = 길이가 d의 배수인 리본의 수 static long[] MultiplesTable(int n) { var count = new int[Limit + 1]; for (int i = 0; i < n; i++) count[int.Parse(tokens[pos++])]++; var table = new long[Limit + 1]; for (int d = 1; d <= Limit; d++) { for (int m = d; m <= Limit; m += d) table[d] += count[m]; // d의 배수만 건너뛰며 본다 } return table; } static void Main() { tokens = Console.In.ReadToEnd().Split((char[])null, StringSplitOptions.RemoveEmptyEntries); int a = int.Parse(tokens[pos++]), b = int.Parse(tokens[pos++]), c = int.Parse(tokens[pos++]); long[] red = MultiplesTable(a); long[] blue = MultiplesTable(b); long[] yellow = MultiplesTable(c); // exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 int를 넘으므로 long var exact = new long[Limit + 1]; for (int d = Limit; d >= 1; d--) // 큰 d부터 { long total = red[d] * blue[d] * yellow[d]; for (int m = 2 * d; m <= Limit; m += d) total -= exact[m]; exact[d] = total; } int q = int.Parse(tokens[pos++]); var output = new StringBuilder(); for (int i = 0; i < q; i++) output.Append(exact[int.Parse(tokens[pos++])]).Append('\n'); Console.Write(output); }}
C++main.cpp · 45 行
#include <iostream>#include <vector>const int LIMIT = 9000;// table[d] = 길이가 d의 배수인 리본의 수std::vector<long long> multiplesTable(int n) { std::vector<int> count(LIMIT + 1, 0); for (int i = 0; i < n; i++) { int x; std::cin >> x; count[x]++; } std::vector<long long> table(LIMIT + 1, 0); for (int d = 1; d <= LIMIT; d++) { for (int m = d; m <= LIMIT; m += d) table[d] += count[m]; // d의 배수만 건너뛰며 본다 } return table;}int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int a, b, c; std::cin >> a >> b >> c; std::vector<long long> red = multiplesTable(a); std::vector<long long> blue = multiplesTable(b); std::vector<long long> yellow = multiplesTable(c); // exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 int를 넘으므로 long long std::vector<long long> exact(LIMIT + 1, 0); for (int d = LIMIT; d >= 1; d--) { // 큰 d부터 long long total = red[d] * blue[d] * yellow[d]; for (int m = 2 * d; m <= LIMIT; m += d) total -= exact[m]; exact[d] = total; } int q; std::cin >> q; for (int i = 0; i < q; i++) { int g; std::cin >> g; std::cout << exact[g] << '\n'; }}
Cmain.c · 43 行
#include <stdio.h>#define LIMIT 9000static int count[LIMIT + 1];static long long red[LIMIT + 1], blue[LIMIT + 1], yellow[LIMIT + 1], exact[LIMIT + 1];/* table[d] = 길이가 d의 배수인 리본의 수 */static void multiples_table(int n, long long *table) { for (int i = 0; i <= LIMIT; i++) count[i] = 0; for (int i = 0; i < n; i++) { int x; if (scanf("%d", &x) != 1) return; count[x]++; } for (int d = 1; d <= LIMIT; d++) { table[d] = 0; for (int m = d; m <= LIMIT; m += d) table[d] += count[m]; /* d의 배수만 건너뛰며 본다 */ }}int main(void) { int a, b, c, q; if (scanf("%d %d %d", &a, &b, &c) != 3) return 0; multiples_table(a, red); multiples_table(b, blue); multiples_table(c, yellow); /* exact[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 int를 넘으므로 long long */ for (int d = LIMIT; d >= 1; d--) { /* 큰 d부터 */ long long total = red[d] * blue[d] * yellow[d]; for (int m = 2 * d; m <= LIMIT; m += d) total -= exact[m]; exact[d] = total; } if (scanf("%d", &q) != 1) return 0; for (int i = 0; i < q; i++) { int g; if (scanf("%d", &g) != 1) return 0; printf("%lld\n", exact[g]); } return 0;}
Craykonmain.cray · 47 行
// 표[d] = 길이가 d의 배수인 리본의 수배수표(길이들, 한계){ 수 = 新列表(한계 + 1, 0); 遍历 (x 在 길이들) { 수[x] += 1; } 표 = 新列表(한계 + 1, 0); 循环 (d = 1; d <= 한계; d++) { 합 = 0; 循环 (m = d; m <= 한계; m += d) { 합 += 수[m]; } 표[d] = 합; } 返回 표;}한계 = 9000;控制台.读行(); // a b c: 줄마다 통째로 읽으니 개수는 쓰지 않는다빨강 = 배수표(控制台.读数字(), 한계);파랑 = 배수표(控制台.读数字(), 한계);노랑 = 배수표(控制台.读数字(), 한계);// 딱[d] = 가장 긴 도막이 딱 d인 묶음의 수. 9000^3은 2^53보다 작아 정확하다딱 = 新列表(한계 + 1, 0);循环 (d = 한계; d >= 1; d--){ 모두 = 빨강[d] * 파랑[d] * 노랑[d]; 循环 (m = 2 * d; m <= 한계; m += d) { 모두 -= 딱[m]; } 딱[d] = 모두;}q = 控制台.读行().转数字();out = [];循环 (i = 0; i < q; i++){ g = 控制台.读行().转数字(); out.添加(딱[g]);}控制台.输出(out.连接("\n"));