文章目录
- [Divisible Numbers (easy version)](https://codeforces.com/contest/1744/problem/E1)
- 问题建模
- 问题分析
- 代码
- [ Divisible Numbers (hard version)](https://codeforces.com/contest/1744/problem/E2)
- 问题建模
- 问题分析
- 1.根据简单版本分析所求
- 2.方法1通过因数分解得到a和b的因数,再获得a * b的因数
- 代码
- 3.方法2分解a和b的质因数,通过质因数组成a*b的两个因数
- 代码
Divisible Numbers (easy version)
问题建模
给定4个数,a,b,c,d,问是否存在两个数x,y, a < x < = c , b < y < = d a<x<=c,b<y<=d a<x<=c,b<y<=d,满足x * y被a * b整除。
问题分析
由于x * y需要包含a * b所有的因数,且x,y还要在一定的范围内,则可以先保证一个数x1在合法位置内,然后通过 a ∗ b / g c d ( a ∗ b , x 1 ) a*b/gcd(a*b,x1) a∗b/gcd(a∗b,x1)获得一个包含a * b剩余因子的数s,然后再将其扩大为比d小的数,在检查是否比b大即可。
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N =2e5+10, Mod = 998244353, P = 2048;
void solve() {
LL a,b,c,d;
cin >>a >>b >>c >>d;
for(LL i=a+1;i<=c;i++){
LL s=a*b/(__gcd(a*b,i));
LL x=d/s*s;
if(x>b){
cout <<i <<" " <<x <<"\n";
return ;
}
}
cout <<-1 <<" " <<-1 <<"\n";
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
Divisible Numbers (hard version)
问题建模
给定4个数,a,b,c,d,问是否存在两个数x,y, a < x < = c , b < y < = d a<x<=c,b<y<=d a<x<=c,b<y<=d,满足x * y被a * b整除。
问题分析
1.根据简单版本分析所求
由于整数的范围扩大,若枚举a+1到c会超时。简单版本中通过 a ∗ b / g c d ( a ∗ b , x 1 ) a*b/gcd(a*b,x1) a∗b/gcd(a∗b,x1)获得一个包含a * b剩余因子的数s,实际上是通过枚举x1得到a * b的一个因子,然后获得另一个因子,然后再放大另外一个因子得到符合范围的数。则实际上我们是要获得a * b的因数。
2.方法1通过因数分解得到a和b的因数,再获得a * b的因数
由于a*b的因数是由a的因数乘b的因数得到的,则可以先得到a,b的因数,然后枚举两个因数的乘积从而得到a * b的因数。然后再像简单版本中的做法类似,将两个因数放大到合法范围。由于1e18内的数,最多有103680个因数,1e9内的数最多有1344个因数。则时间复杂度为O( a + b + ( 1344 ) 2 \sqrt{a}+\sqrt{b}+(1344)^2 a+b+(1344)2)
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N = 2e5 + 10, Mod = 998244353, P = 2048;
void solve() {
LL a, b, c, d;
cin >> a >> b >> c >> d;
vector<LL> v1, v2;
///获得a,b的因数
for (int i = 1; i <= a / i; i++) {
if (a % i == 0) {
v1.push_back(i),v1.push_back(a / i);
}
}
for (int i = 1; i <= b / i; i++) {
if (b % i == 0) {
v2.push_back(i),v2.push_back(b / i);
}
}
for (auto i : v1) {
for (auto j : v2) {
///获得a*b的两个因数,然后将其放大到合法的范围
LL x = i * j,y=a*b/x;
x*=c/x;
y*=d/y;
if(x>a&&y>b){
cout <<x <<" " <<y <<"\n";
return ;
}
}
}
cout << -1 << " " << -1 << "\n";
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}
3.方法2分解a和b的质因数,通过质因数组成a*b的两个因数
由于a*b的因数是由a的部分质数乘b的部分质数得到的,则可以先得到a,b的各个质数,然后通过DFS分配各个质数的数量,从而得到a * b的两个因数。然后再像简单版本中的做法类似,将两个因数放大到合法范围。1e18的数质数组合能产生的因数数量大概为1e5,故不会超时。
代码
#include<bits/stdc++.h>
#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N = 2e5 + 10, Mod = 998244353, P = 2048;
LL a,b,c,d;
map<int,int> mp;
vector<PII> primes;
void divide(int n){
for(int i=2;i<=n/i;i++){
while(n%i==0) mp[i]++,n/=i;
}
if(n>1) mp[n]++;
}
bool dfs(int u,LL x,LL y){
if(u==primes.size()){
x*=c/x;
y*=d/y;
if(x>a&&y>b){
cout <<x <<" " <<y <<"\n";
return true;
}else return false;
}
///分配当前质数的数量到两个因数当中
int p=primes[u].x,cnt=primes[u].y;
for(int i=1;i<=cnt;i++) y*=p;
for(int i=0;i<=cnt;i++){
if(dfs(u+1,x,y)) return true;
x*=p,y/=p;
}
return false;
}
void solve() {
mp.clear(),primes.clear();
cin >>a >>b >>c >>d;
///先得到a,b的各个质数数量
divide(a),divide(b);
for(auto v:mp) primes.push_back(v);
if(!dfs(0,1,1)) cout <<-1 <<" " <<-1 <<'\n';
}
int main() {
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}