你考虑,我们观察数据范围,发现可以是 \(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);
}
}
标签:Count,NN,int,题解,数对,sqrt,枚举,&&,CF1830B
From: https://www.cnblogs.com/rickylin/p/17691853.html