xtuoj 方程

题目

思路

一、核心逻辑(先立框架,再填细节)

核心:把 "解复杂方程" 转化为 "找因数对"

  1. 数学变形:将二元嵌套方程转化为 "两数相乘等于定值",实现从 "难" 到 "易" 的突破;
  2. 高效枚举:利用因数对称性缩小枚举范围,避免超时;
  3. 约束过滤:筛选符合 "x/y 非负" 的有效解,按要求整理输出。

二、逐环节拆解(带思考 + 实操 + 细节)

环节 1:为什么必须做数学变形?(吃透原理)

直接求解的痛点

原方程 xy + ax + b*y = c 中,x 和 y 相互嵌套:

  • 若直接枚举 x,需通过 y = (c - a*x)/(x + b) 计算 y,需同时满足:
    1. 分母 x + b ≠ 0(恒成立,因 x≥0、b 为正整数);
    2. 分子 c - a*x ≥ 0;
    3. 分子能被分母整除;
  • 当 c 取 1e9 时,枚举 x 的范围达 1e9 次,必然超时。
变形过程(记死配凑技巧)
  1. 原方程:xy + ax + b*y = c
  2. 两边加 ab(配凑因式分解的关键,目的是分组提公因子): x y + ax + b y + ab = c + ab
  3. 分组提取公因子:x (y + a) + b (y + a) = c + ab → (x + b)(y + a) = c + ab
变量替换与约束转化
  • 令 A = x + b,B = y + a,方程简化为:AB = target(其中 target = c + ab);
  • 由 x≥0、y≥0 推导约束条件:
    • x = A - b ≥ 0 → A ≥ b(等号不可丢!A=b 时 x=0,是合法解);
    • y = B - a ≥ 0 → B ≥ a(同理,B=a 时 y=0,是合法解)。

环节 2:如何高效找因数对 (A,B)?(落地操作)

因数的核心性质(效率关键)

若 A 是 target 的因数,则 B = target/A 必为 target 的因数,且 A 和 B 中必有一个≤√target。

  • 示例:target=20,√20≈4,枚举 A=1、2、4 即可覆盖所有因数对:(1,20)、(2,10)、(4,5),枚举次数从 20 次降至 3 次。
实操步骤
  1. 枚举范围:A 从 1 到√target(用 A*A ≤ target 替代 A ≤ √target,避免浮点精度误差,如√100 可能算出 9.99999);
  2. 因数判断:若 target % A == 0,则 A 是因数,计算对应 B=target/A;
  3. 有效解检查:
    • 检查 (A,B):若 A ≥ b 且 B ≥ a,则还原解 x = A - b、y = B - a,存入结果集;
    • 检查 (B,A):若 A≠B(避免平方数重复存解,如 target=16,A=4、B=4),且 B ≥ b 且 A ≥ a,则还原另一组解,存入结果集。

环节 3:避坑细节(关键保障,必看)

坑点 典型表现 解决办法
数据溢出 a=1e5、b=1e5 时,a*b=1e10 超 int 范围,target 计算错误 用 long long 存储 target、A、B;计算时先强制转换:(long long) a * b
约束搞反 误将条件写为 A ≥ a、B ≥ b 死记:A 对应 b(x=A-b),B 对应 a(y=B-a)
漏等号 条件写为 A > b、B > a,漏掉 x=0 或 y=0 的解 严格写 A ≥ b、B ≥ a
解重复 target 为平方数时(如 16),同一组解存两次 仅当 A≠B 时,才检查 (B,A)
解无序 枚举结果 x 乱序,不符合输出要求 用数组存解后,按 x 升序排序

三、核心记忆点(一眼回忆全流程)

  1. 变形公式:方程 + ab → (x+b)(y+a)=c+ab;
  2. 枚举边界:A 遍历到√target,用 A*A≤target 避浮点误差;
  3. 约束条件:A≥b、B≥a,等号不可丢;
  4. 避坑关键:long long 防溢出,A≠B 去重,排序保顺序。

四、同类题迁移思路(遇到新题怎么想)

  1. 观察方程:能否通过配凑、因式分解转化为 "简单乘积形式";
  2. 约束转化:将原变量的限制(非负、范围)转化为新变量的约束;
  3. 效率优化:利用数学性质(因数对称、质数筛等)缩小枚举范围;
  4. 边界处理:解决溢出、重复、等号等细节问题,按题目要求整理输出。

代码

cpp 复制代码
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

typedef struct {
	int x;
	int y;
} Solution;

// 比较函数,用于按x从小到大排序(其实枚举时已经有序,可省略)
int cmp(const void *a, const void *b) {
	return ((Solution*)a)->x - ((Solution*)b)->x;
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		long long target = (long long)c + (long long)a * b; // 防止溢出
		Solution sols[10000]; // 存储解,足够大
		int cnt = 0;
		
		// 枚举所有因数A
		for (long long A = 1; A * A <= target; A++) {
			if (target % A == 0) {
				long long B = target / A;
				// 检查A和B是否满足条件
				if (A >= b && B >= a) {
					sols[cnt].x = A - b;
					sols[cnt].y = B - a;
					cnt++;
				}
				// 处理A≠B的情况,避免重复
				if (A != B) {
					if (B >= b && A >= a) {
						sols[cnt].x = B - b;
						sols[cnt].y = A - a;
						cnt++;
					}
				}
			}
		}
		
		// 按x排序(可选,因为枚举A是从小到大的)
		qsort(sols, cnt, sizeof(Solution), cmp);
		
		// 输出结果
		printf("%d\n", cnt);
		for (int i = 0; i < cnt; i++) {
			printf("%d %d\n", sols[i].x, sols[i].y);
		}
	}
	return 0;
}
相关推荐
IT_Octopus1 小时前
算法题:力扣 热题100道 中等难度128. 最长连续序列
算法·leetcode
Swift社区1 小时前
LeetCode 441 - 排列硬币
算法·leetcode·职场和发展
TL滕1 小时前
从0开始学算法——第七天(快速排序算法练习)
笔记·学习·算法·排序算法
MicroTech20251 小时前
MLGO微算法科技 D-S融合算法技术发布,助力脑机接口迈向实用化
大数据·科技·算法
Q741_1471 小时前
C++ 栈 模拟 力扣 844. 比较含退格的字符串 题解 每日一题
c++·算法·leetcode·模拟·
CoderYanger1 小时前
动态规划算法-简单多状态dp问题:14.粉刷房子
开发语言·算法·leetcode·动态规划·1024程序员节
张张努力变强1 小时前
二叉树——精选题目,体验递归的暴力美学!
c语言·数据结构·算法
FMRbpm1 小时前
栈练习--------(LeetCode 739-每日温度)
数据结构·c++·算法·leetcode·新手入门
子一!!1 小时前
数据结构==二叉平衡树,AVL树 ===
数据结构·算法