2024.9.27校测

JPGOJCZX / 2024-09-28 / 原文

T1

题目描述

\(Mr.Hu\) 开了个饭店,来了两位客人:\(Alice\)\(Bob\),他们吃完饭要结账时,发现他们需要支付 \(c\) 元钱,但是 \(Alice\) 只有面值为 \(a\) 的钱,\(Bob\) 只有面值为 \(b\) 的钱(他们每个人的钱的和都大于 \(c\), 即可以认为他们有无数张对应面值钱)。现在,\(Mr.Hu\) 想知道,他们可能刚好支付完饭钱吗?如果可能,那么有多少种方式?你还需要计算出他们所有可能的支付方式的支付的钱的张数的和。

输入格式

\(1\) 行包含 \(2\) 个整数:\(T, opt\),其中 \(T\) 表示数据组数,\(opt\) 为数据类型。

接下来 \(T\) 行,每行 \(3\) 个整数:\(a, b, c\)

输出格式

对于每组数据:

  • 如果 \(opt = 1\),输出一行,包含一个整数:\(A\),其中 \(A\) 表示刚好支付的方案数。

  • 如果 \(opt = 2\),输出一行,包含两个整数:\(A, B\),其中 \(A\) 表示刚好支付的方案数,\(B\) 表示所有可能
    支付方式的张数和。

输入样例1

2 2
3 4 21
2 4 12

输出样例1

2 13
4 18

样例1解释

对于 \(3, 4, 21\),一共有两种可能的支付方式,分别是:\((3, 3), (7, 0)^1\),所以 \(A\)\(2\)\(B\)\(3 + 3 + 7 + 0 = 13\)

对于 \(2, 4, 12\),一共有四种可能的支付方式,分别是:\((6, 0), (4, 1), (2, 2), (0, 3)\),所以 \(A\)\(4\)\(B\)
\(6 + 0 + 4 + 1 + 2 + 2 + 0 + 3 = 18\)

输入样例2

2 1
3 4 21
2 4 12

输出样例2

2
4

数据规模

对于 \(20\%\) 数据,\(1 \leq a, b, c \leq 10000, 1 \leq T \leq 1000\)

对于另外 \(40\%\) 数据,\(1 \leq a, b, c \leq 10^9\),其中 \(opt = 1\)

对于另外 \(40\%\) 数据,\(1 \leq a, b, c \leq 10^9\),其中 \(opt = 2\)

对于 \(100\%\) 数据,\(1 \leq T \leq 10^5, 1 \leq opt \leq 2\)

题解

\(Alice\) 要支付的钱的张数为 \(x\) (\(x \geq 0\)),\(Bob\) 要支付的钱的张数为 \(y\) (\(y \geq 0\)),那么可以写出如下二元一次不定方程 \(ax + by = c\),根据数论学习笔记(一)(2024.7.25),当且仅当 \(\gcd(a, b) \mid c\) 时才有解。

考虑 \(x\)\(y\) 都要大于 \(0\),那么将 \(x\) 变为最小的满足条件的非负整数时,如果 \(y\) 仍未非正整数,那么也是凑不出这么多钱的。

以上两种情况是无解的,直接输出 \(0\),如果有 \(x\)\(y\) 都为非负整数的解,那么考虑通解 \(x = x_0 + (b / d)n, y = y_0 - (a / d)n\)

考虑到将 \(x\) 变为最小值时 \(y\) 已是最大值,所以 \(\lfloor\displaystyle\frac{y}{a / d}\rfloor + 1\) (加 \(1\) 是因为可以一个 \(a / d\) 也不减) 就是方案数。

考虑每变化一次张数会变大或变小 \(| a / d - b / d |\),且要么一直变大,要么一直变小,可以发现这是一个等差数列,所以只需要将总张数最大和总张数最小的情况算出,就可以套用等差数列求和公式来求解张数。

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int extend_gcd(int a, int b, int &x, int &y){
	if(b == 0){
		x = 1;
		y = 0;
		return 0;
	}
	int d = extend_gcd(b, a % b, y, x);
	y -= a / b * x;
	return d;
}
int gcd(int a, int b){
	return b ? gcd(b, a % b) : a;
}
int T, opt, a, b, c;
signed main(){
	freopen("pay.in", "r", stdin);
	freopen("pay.out", "w", stdout);
	scanf("%lld%lld", &T, &opt);
	while(T--){
		scanf("%lld%lld%lld", &a, &b, &c);
		if(c % gcd(a, b) != 0){
			if(opt == 1)
				printf("0\n");
			else
				printf("0 0\n");
			continue;
		}	
		int d = gcd(a, b);
		int x, y;
		extend_gcd(a, b, x, y);
		x *= c / d;
		y *= c / d;
		int dis;
		if(x < 0){
			dis = ceil((-x) * 1.0 / (b / d));
			x += dis * (b / d);
			y -= dis * (a / d);
		} else {
			dis = floor(x * 1.0 / (b / d));
			x -= dis * (b / d);
			y += dis * (a / d);
		}
		if(y >= 0){
			printf("%lld", y / (a / d) + 1);
			if(opt == 1)
				printf("\n");
			else {
				printf(" ");
				int ans1 = x + y, ans2 = y % (a / d) + x + y / (a / d) * (b / d);
				int ans = (ans1 + ans2) * (y / (a / d) + 1) / 2;
				printf("%lld\n", ans);
			}
		} else {
			if(opt == 1)
				printf("0\n");
			else
				printf("0 0\n");
			continue;
		}
	}
	return 0;
}

T2

题目描述

\(Mr.Hu\) 被传送到了一个无限大的表格上,现在这个表格的第 \(i\) 行第 \(j\) 列的值是 \(a_{i,j}(0 \leq i, j)\)

\[a_{i,j} = \begin{cases} 1 & : j = 0 或 i = j\\ a_{i - 1, j} + a_{i - 1, j - 1} & : 0 < j < i\\ 0 & : j > i \end{cases} \]

现在,\(Mr.Hu\) 站在 \((n, m)\) 这个位置,他想知道,他向上或向左上方 \(45\) 度望去,看到的数的和是多少。

\((n, m)\) 向上望去,他会看到 \((n, m), (n − 1, m), (n − 2, m), \dots,(0, m)\) 这些位置。

\((n, m)\) 向左上方 \(45\) 度望去,他会看到 \((n, m), (n − 1, m − 1), \dots\),直到某一维的下标变为 \(0\)

这个数可能很大,你只需将答案对 \(10^9 + 7\) 取模即可。

输入格式

\(1\) 行一个整数:\(T\),表示数据组数。

接下来 \(T\) 行,每行格式为:dir n m,其中 \(dir\)\(1\) 表示向上看,\(2\) 表示向左上方看,\((n, m)\)\(Mr.Hu\) 现在的位置。

输出格式

对于每组数据,输出一行表示答案。

输入样例

2
1 3 2
2 3 2

输出样例

4
6

样例解释

表格左上角长成这样(行列都是从 \(0\) 开始的):

\[1 \,\, 0 \,\, 0 \,\, 0\\ 1 \,\, 1 \,\, 0 \,\, 0\\ 1 \,\, 2 \,\, 1 \,\, 0\\ 1 \,\, 3 \,\, 3 \,\, 1 \]

这样从 \((3, 2)\) 向上看,会看到:\(3, 1, 0, 0\),和为 \(4\)

向左上角看,会看到:\(3, 2, 1\),和为 \(6\)

数据规模

对于 \(30\%\) 数据,\(1 \leq n, m \leq 5000, 1 \leq T \leq 1000\)

对于 \(100\%\) 数据,\(1 \leq n, m \leq 10^6, 1 \leq T \leq 50000\)

T3

题目描述

\(Mr.Hu\) 觉得在学习过程中,需要举一反三,做一题要理解透,然后遇到相似的问题时能类似地转化。所以想了一道和以前类似的题目,相信聪明如你,肯定能轻而易举地解决。

\(Mr.Hu\) 会给你 \(n\) 个非负整数,然后从中选 \(k\) 个出来,然后把这 \(k\) 个数按位或起来,\(Mr.Hu\) 想知道有多少种选法,使得或起来的结果为 \(r\)

输入格式

\(1\) 行一个整数 \(T\),表示测试组数。

接下来 \(T\) 组数据,对于每组数据。

\(1\) 行两个整数 \(n, k, r\)

接下来 \(1\) 行包含 \(n\) 个非负整数:\(a_1, a_2, \dots, a_n\)

输出格式

对于每组数据,输出一行,包含一个整数,即方案数,因为结果可能很大,只需要对 \(10^9 + 7\) 取模即可。

输入样例

2
4 2 3
1 2 3 4
4 1 1
1 2 3 4

输出样例

3
1

样例解释

对于第一组数据,一共有 \(3\) 种选法:\((1, 2), (1, 3), (2, 3)\)

对于第二组数据,一共有 \(1\) 种选法:\((1)\)

数据规模

对于 \(10\%\) 数据,\(1 \leq n \leq 10, 0 \leq a_i < 2^{10}\)

对于 \(30\%\) 数据,\(1 \leq n \leq 100, 0 \leq a_i < 2^{10}\)

对于 \(50\%\) 数据,\(1 \leq n \leq 10^5, 0 \leq a_i < 2^{15}\)

对于 \(100\%\) 数据,\(1 \leq n \leq 10^5, 0 \leq a_i < 2^{20}, 1 \leq k \leq n, 1 \leq T \leq 5\)