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;
}
posted @ 2024-12-16 12:41  shen_kong  阅读(135)  评论(0)    收藏  举报