Insane Problem(思维)
Wave is given five integers \(k\), \(l_1\), \(r_1\), \(l_2\), and \(r_2\). Wave wants you to help her count the number of ordered pairs \((x, y)\) such that all of the following are satisfied:
- \(l_1 \leq x \leq r_1\).
- \(l_2 \leq y \leq r_2\).
- There exists a non-negative integer \(n\) such that \(\frac{y}{x} = k^n\).
Input
The first line contains an integer \(t\) (\(1 \leq t \leq 10^4\)) — the number of test cases.
The only line of each test case contains five integers \(k\), \(l_1\), \(r_1\), \(l_2\), and \(r_2\) (\(2 \leq k \leq 10^9, 1 \leq l_1 \leq r_1 \leq 10^9, 1 \leq l_2 \leq r_2 \leq 10^9\)).
Output
For each test case, output the number of matching ordered pairs \((x, y)\) on a new line.
题意:给一个 k (k >= 2),再给两个区间,问有多少对数,(x, y)满足 y / x 是 k 的自然数数幂倍。
例如:
3 5 7 15 63
其中
- \((5,15)\)
- \((5,45)\)
- \((6,18)\)
- \((6,54)\)
- \((7,21)\)
- \((7,63)\)
是符合要求的
2 2 6 2 12
其中
- \((2,2)\)
- \((2,4)\)
- \((2,8)\)
- \((2,16)\)
- \((3,3)\)
- \((3,6)\)
- \((3,12)\)
- \((4,4)\)
- \((4,8)\)
- \((5,5)\)
- \((5,10)\)
- \((6,12)\)
是符合要求的。
思路:
一开始想暴力,于是先试了试:
#include<bits/stdc++.h>
#define L(i, j, k) for(int i = (j); i <= (k); ++i)
#define R(i, j, k) for(int i = (j); i >= (k); --i)
#define sz(a) ((int) (a).size())
#define pb emplace_back
#define me(a, x) memset(a, x, sizeof(a))
#define vi vector<int>
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
using i128 = __int128;
#define TEST
#define TESTS int T; cin >> T; while(T--)
const int N = 1E+6;
using namespace std;
void Main() {
i64 ans = 0;
int k, l1, r1, l2, r2;
cin >> k >> l1 >> r1 >> l2 >> r2;
int left = min(r1, r2 / k);
L(i, l1, left) {
i64 num = i;
while(num <= r2) {
if(l2 <= num and num <= r2) ans++;
num *= k;
}
}
cout << ans << endl;
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
TESTS Main();
return 0;
}
很明显超时了,
后来又想到对于每个 i ,他的下一个阶段是 i * k ,所以可以将 a 区间分段处理,后一段的长度是前一段的 k 倍,这样要处理的 i 就很少。但是实际上一个区间的 i 的答案有可能越来越少,实际上跟暴力是一样的。
后来我想得到了逆向思维,不是对于每一个 a 区间的数去找他对应的 b 区间的数能够成几对,而是对于每一个 b 区间的数去找他对应的 a 区间的数能够成几对。不对,这不还是一样的吗。
应该说对于 k 的所有次幂,找左右区间重合的数有多少个。
例如:
- k 的次幂 = 0,找 a b 区间重合的数有多少个。
- k 的次幂 = 1,将 b 区间左右端点同时除以 k ,再找 a b 区间重合的数有多少个。
- k 的次幂 = 2,将 b 区间左右端点再除以 k ,再找 a b 区间重合的数有多少个。
and so on......
有个小细节导致WA了,周赛遗憾退场
b 区间的左端点向上取整,右端点向下取整。
#include<bits/stdc++.h>
#define L(i, j, k) for(int i = (j); i <= (k); ++i)
#define R(i, j, k) for(int i = (j); i >= (k); --i)
#define sz(a) ((int) (a).size())
#define pb emplace_back
#define me(a, x) memset(a, x, sizeof(a))
#define vi vector<int>
using i64 = long long;
using u64 = unsigned long long;
using u32 = unsigned int;
using i128 = __int128;
#define TEST
#define TESTS int T; cin >> T; while(T--)
const int N = 1E+6;
using namespace std;
void Main() {
i64 ans = 0;
i64 k, l1, r1, l2, r2;
cin >> k >> l1 >> r1 >> l2 >> r2;
while(r2 >= l1) {
//cout << l2 << ' ' << r2 << endl;
i64 now = min(r1, r2) - max(l1, l2) + 1;
if(now <= 0) now = 0;
ans += now;
//cout << ans << endl;
r2 = (r2) / k;
l2 = (l2 + k - 1) / k;
}
cout << ans << endl;
}
int main() {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
TESTS Main();
return 0;
}

浙公网安备 33010602011771号