题意:
Given two positive integers a and b,find suitable X and Y to meet the conditions:
X+Y=a
Least Common Multiple (X, Y) =b
Input
Input includes multiple sets of test data.Each test data occupies one line,including two positive integers a(1≤a≤2*104),b(1≤b≤109),and their meanings are shown in the description.Contains most of the 12W test cases.
Output
For each set of input data,output a line of two integers,representing X, Y.If you cannot find such X and Y,output one line of “No Solution”(without quotation).
Sample Input
6 8
798 10780
Sample Output
No Solution
308 490
翻译成汉语,就是给出两个数a,b (a <= 2e4, b <= 1e9),问是否存在x,y,使得x + y = a, x和y的最小公倍数为b?一共有12万组样例!!
思路:
由于有12W组样例,只要每组样例超过1e3几乎就凉了,因此这个题肯定不能暴力。于是我想到了第一种方法:
方法一:先利用素数筛把sqrt(1e9)以内的所有素数筛出来,然后求出b的所有种素因子和每种素因子的个数。
然后dfs每种素因子(每种素因子可以取0~a[i]个),再加上各种剪枝。。。982ms飘过~~
方法一代码耗时982ms实在是太吓人了,万一现场赛评测机一个不高兴给你慢个20ms你就嗝屁了,然后看到网上正解,一拍脑瓜子。哎,智商压制啊~
方法二:我们设x和y的最大公约数为c,那么x = i ✖️ c,y = j ✖️ c。
由于c是x,y的最大公约数,所以i,j一定互质(要是两者不互质存在公约数的话,x,y的最大公约就为c✖️i和j的公约数,而不是c了,因此i,j一定互质)
那么:
a = x + y = (i ✖️ c) + (j✖️c)= (i + j) ✖️ c
b = lcm(x, y) = x ✖️ y / gcd(x, y) = x ✖️ y / c = (i ✖️ c ✖️ j ✖️ c) / c = i ✖️j✖️c
由于i,j互质,那么(i + j)和 (i * j)也一定互质。我们可以用反证法证明:
若(i + j),(i✖️j)不互质存在一个素因子t:那么在i✖️j中,t只能是 i的素因子 或 j的素因子(因为i,j互质,不存在公共素因子);又因为i和j必须都存在素因子t才能使得(i + j)存在素因子t(相当于同余定理,若a % t == 0, 则b % t == 0才能使得(a + b)% t == 0),因此和i 存在素因子t 或 j存在素因子t相矛盾,所以(i + j)和(i * j)互质
因此gcd(a, b) = c = gcd (x, y)
因此化简得:
x + y = a (一)
x✖️y = b ✖️ gcd(x, y) = b ✖️gcd(a, b) (二)
a,b已知,因此就相当于解二元一次方程,把方程(一)带入(二)得:
x✖️(a - x) = b ✖️gcd(a, b)
=> x^2 - a✖️x + b✖️gcd(a,b) = 0
=> x = (a + sqrt(a^2 - 4✖️b✖️gcd(a,b))) / 2
就可以求出x,y了。这样代码简短,而且耗时只有124ms:
Orz~
代码:
方法一:
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#define d int32_t
#define ll int64_t
#define N 32000
#define mem(a) memset(a, 0, sizeof(a))
#define For(i, star, endd) for(d i = star; i <= endd; i++)
bool book[N + 5];
int prime[N + 5];
ll ans[105];
ll ansnum[105];
ll a, b;
bool flag;
//预处理素数
void init() {
mem(book);
mem(prime);
For(i, 2, N) {
if (!book[i]) {
prime[++prime[0]] = i;
}
For(j, 1, prime[0]) {
ll t = prime[j] * i;
if (t > N) break;
book[t] = 1;
if (i % prime[j] == 0) break;
}
}
}
//求最大公约数
ll gcd (ll y, ll x) {
ll c = y % x;
return c == 0? x: gcd(x, c);
}
//枚举不同素数组成的因子可能
void dfs(ll now, ll n, ll val) {
if (flag) return;
if (val > a / 2) return;
if(now == n + 1) {
if(val * (a - val) / gcd((a - val), val) == b) {
printf("%lld %lld\n", val, a - val);
flag = 1;
}
return;
}
ll res = 1;
dfs(now + 1, n, val);
For(i, 1, ansnum[now]) {
res *= ans[now];
dfs(now + 1, n, val * res);
}
}
d main () {
init();
while (scanf("%lld%lld", &a, &b) == 2) {
mem(ansnum);
ll tot = 0, bb = b;
flag = 0;
For(i, 1, prime[0]) {
if(bb % prime[i] == 0) {
ans[++tot] = prime[i];
while(bb % prime[i] == 0) {
ansnum[tot]++;
bb /= prime[i];
}
}
if (bb == 1) break;
}
if(bb != 1) {
ans[++tot] = bb;
ansnum[tot] = 1;
}
dfs(1, tot, 1);
if(!flag) {
printf("No Solution\n");
}
}
return 0;
}
方法二:
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <math.h>
#include <algorithm>
#define d int32_t
#define ll int64_t
#define mem(a) memset(a, 0, sizeof(a))
#define For(i, star, endd) for(d i = star; i <= endd; i++)
using namespace std;
ll gcd (ll y, ll x) {
ll c = y % x;
return c == 0? x: gcd(x, c);
}
d main () {
ll a, b;
while (scanf("%lld%lld", &a, &b) == 2) {
ll c = gcd(max(a, b), min(a, b));
double x = (sqrt(a * a - 4 * b * c) + a) / 2;
if (x == (ll)x) {
ll t = (ll)x;
printf("%lld %lld\n", min(t, a - t), max(t, a - t));
} else {
printf("No Solution\n");
}
}
return 0;
}
如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢