【题解】CF1830B The BOSS Can Count Pairs

发布时间 2023-09-10 20:37:38作者: ricky_lin

你考虑,我们观察数据范围,发现可以是 \(O(n\sqrt n) / O(n\log n)\) 的,我们又看到乘法,便有几个大概的想法:

  • 数论分块
  • \(O(\sqrt n)\) 枚举其中一个乘数
  • 还有什么……(笔者学识浅陋,读者可以帮忙补充)

我们可以找到两种 \(O(n^2)\) 做法:

  • \(O(n^2)\) 枚举数对 \((i,j)\) 然后进行判断。(这个很好想,就是平常的暴力)
  • \(O(n^2)\) 一个 \(n\) 枚举 \(a_i\) 的值,并将 \(b_i\) 记录在桶中,另一个 \(n\) 枚举 \(j\) 并在桶中查找 \(a_i \times a_i - b_j\) (有时候换一种枚举方式 确实不失为一种打开新思路的好方法)

我们可以发现 \(a_i \times a_j\) 是不大于 \(2n\) 的,所以里面最小的数是不大于 \(\sqrt {2n}\) 的,然后我们就可以将第一维从 \(O(n)\) 变为 \(O(\sqrt n)\)

当然细节上还是需要处理一下,因为每个数对只能出现一次,所以我们让 \(a\) 小的在前面,\(a\) 大的在后面,\(a\) 相同再是按下标(说得有点玄乎?看不懂可以直接看代码,代码还是很好懂的)。

然后我们就做完了这道题。

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 2e5 + 8; 
typedef long long ll;
int T;
int n;
int a[NN],b[NN];
int cnt[NN << 1];
int main(){
	scanf("%d",&T);
	while(T--){
		ll ans = 0;
		scanf("%d",&n);
		for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
		for(int i = 1; i <= n; ++i) scanf("%d",&b[i]);
		int c = sqrt(2 * n) + 1;
		for(int j = 1; j <= c; ++j){
			for(int i = 0; i <= n; ++i) cnt[i] = 0;
			for(int i = 1; i <= n; ++i) if(a[i] == j){
				if(j*j - b[i] >= 0 && j * j - b[i] <= n) ans += cnt[j*j-b[i]];
				++cnt[b[i]];
			} 
			for(int i = 1; i <= n; ++i){
				if(a[i]*j-b[i] >= 0 && a[i] > j && a[i] * j - b[i] <= n) ans += cnt[a[i]*j-b[i]];
			}
		}
		printf("%lld\n",ans);
	}
}