1. 程式人生 > 實用技巧 >F. Equal Product (數學,思維,暴力)

F. Equal Product (數學,思維,暴力)

題目:傳送門

題意

給你 n,m,l,r;你需要找到 (x1, x2, y1, y2) 這樣的四元組,滿足:

1 <= x1 < x2 <= n;

1 <= y2 < y1 <= m;

x1*y1 = x2*y2;

l <= x1*y1 <= r;

對於每個x1=1,2,....n; 輸出任意一個滿足條件的四元組,若不存在這樣的四元組,輸出 -1;

1<=n,m<=2e5; 1 <= l <= r <= n * m;

思路

對於 x1*y1 = x2*y2; 我們假設 x1 < x2,可以證明總是存在一對(a,b) (a | x1, b | y1, a < b) 使得 x2 = x1 / a * b; y2 = y1 / b * a;

我可以列舉 x1,然後知道了 x1 就確定了 y1 的取值範圍,由於 a 是 x1 的因子,我們可以列舉 a,通過 a 去 確定 b,若 b 確定了,則所有數都確定了。

我們可以在 o(nlogn) 的時間內算出所有的 (x1, a);

然後我們列舉 x1,通過 x1 可以確定 y1 的取值範圍,是一段區間,並且隨著 x1 的增大,這個區間一直在收縮;對於這個區間的維護,我們可以用兩個指標,指向這段區間的左端和右端;然後隨著 x1 的變化,動態的維護這段區間即可。

我們可以用 set 存 y1 的所有所有可能取值的所有因子,這樣, set裡面存的數就是 b 的可能取值。

對於每個 a,若 x1 / a * b <= n;則存在答案;所以,若第一個大於a的b滿足 x1 / a * b <= n;就更新 x1 的答案;

我們可以通過 upper_bound 找到 set 中第一個大於 a 的 b;

#include <bits/stdc++.h>
#define LL long long
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k;i >= j; i--)
#define pb push_back
#define make make_pair
#define fir first
#define sec second
using namespace std;

const
int N = 1e6 + 5; LL n, m; LL l, r; vector < LL > Y[N]; pair < LL, LL > ans1[N], ans2[N]; set < LL > Q; vector < LL > id(N, 0); vector < LL > cnt(N, 0); void solve() { scanf("%lld %lld %lld %lld", &n, &m, &l, &r); rep(i, 1, 200000) { /// 列舉所有 (x1, a) for(LL j = i; j <= 200000; j += i) { Y[j].pb(i); } } LL nowl = m + 1; /// y1的可能取值區間的左端點; LL nowr = m; /// y1 的可能取值區間的右端點 rep(x1, 1, n) { /// 列舉 x1 LL LY = (l + x1 - 1) / x1; /// 確定當前的 y1 可能取值區間的左端點; LL RY = r / x1; /// 確定當期 y1 的可能取值區間的右端點 while(nowl > LY) { /// 移動左指標,維護 y1 的可能取值區間 nowl--; for(int v : Y[nowl]) { if(cnt[v] == 0) Q.insert(v); cnt[v]++; id[v] = nowl; } } while(nowr > RY) { /// 移動右指標 for(int v : Y[nowr]) { cnt[v]--; if(cnt[v] == 0) Q.erase(v); } nowr--; } for(auto a : Y[x1]) { /// 列舉 a auto v = Q.upper_bound(a); if(v == Q.end()) continue; int b = *v; if(x1 / a * b <= n) { /// 滿足條件,更新答案 int y1 = id[b]; ans1[x1] = make(x1, y1); ans2[x1] = make(x1 / a * b, y1 / b * a); } } } rep(i, 1, n) { if(ans1[i].fir != 0) { printf("%lld %lld %lld %lld\n", ans1[i].fir, ans1[i].sec, ans2[i].fir, ans2[i].sec); } else puts("-1"); } } int main() { // int _; scanf("%d", &_); // while(_--) solve(); solve(); return 0; }