Pairs
Description
Given N integers,count the number of pairs of integers whose sum is less than K. And we have Mqueries.
Input
The first behavioris an integer T (1 < = T < = 10), on behalf of the number of sets ofdata. Each group of data is the first line N, M (1 < = n, m < = 100000),The next line will give N integers. a[i] (0<=a[i]<=100000).
The next M lines,each line contains the query K (1 <= K<=200000).
Output
For each query,output the number of pairs of integers whose sum is less than K.
Sample Input
1
5 2
1 5 3 4 2
5
7
Sample Output
2
6
这题确实很巧妙,开始以为能hash一下,但仔细分析后感觉还是会超时,一直没有什么好办法做,看了题解之后,居然是用fft(快速傅立叶变换)做的,花了一下午+一晚上,还没把傅立叶看懂,但是勉强把fft的模板看懂了,大体上明白这个题的思路,fft的作用无非就是求个卷积,这个卷积取余进位就是nlogn的大数乘法,本质上就是求两个序列
积的系数,大数的乘法也是上面这个表达式的特殊情况,xi表示位数
回到这个题,hash的思想其实没有问题,先把任意两个数的和存在一个表里,然后前缀和, q*O(1)的时间复杂度
但是hash的预处理却要O(N*N) ,这里就要用fft进行优化,
预处理的时候我们是把每一个数,与其他所有的数加了一次 , 这里把一个集合考虑成完全相同的两个,不就等价于从一个集合选一个元素,与另外一个集合所有元素相加一次
类比大数的乘法, 12345*78912 计算这个的时候不就是把下面每一个数乘了上面一次吗?
所以大数的乘法是怎么优化的,这个题就是怎么优化的,
只不过要先对输入的数据做一些处理,
for (int i = 1;i <= n;++i ){
scanf("%d",&nub[i]);
x1[nub[i]]++;
}
要把数据转化为hash的形式,把两个相同的x1序列作为参数进行快速傅立叶变换后得到的sum
solve(x1, l+1, x1, l+1, sum);
sum[i] 的意思是其实就是两个数相加等于i的个数 , 只不过里面有重复
第一个重复是在把一个集合考虑成两个集合的时候,可以同时选取了同一个元素x, 这个时候要在sum中减去这个数的两倍
即 sum[x+x] --;
第二个重复是这里考虑了顺序,但其实选取的两个元素本身没有顺序,所以除2即可
最后用前缀和处理一下就行了
注意sum序列边界的选取
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
#define L(x) (1 << (x))
const double PI = acos(-1.0);
const int Maxn = 133015;
double ax[Maxn], ay[Maxn], bx[Maxn], by[Maxn];
char sa[Maxn/2],sb[Maxn/2];
int sum[Maxn];
int x1[Maxn],x2[Maxn];
int revv(int x, int bits)
{
int ret = 0;
for (int i = 0; i < bits; i++)
{
ret <<= 1;
ret |= x & 1;
x >>= 1;
}
return ret;
}
void fft(double * a, double * b, int n, bool rev)
{
int bits = 0;
while (1 << bits < n) ++bits;
for (int i = 0; i < n; i++)
{
int j = revv(i, bits);
if (i < j)
swap(a[i], a[j]), swap(b[i], b[j]);
}
for (int len = 2; len <= n; len <<= 1)
{
int half = len >> 1;
double wmx = cos(2 * PI / len), wmy = sin(2 * PI / len);
if (rev) wmy = -wmy;
for (int i = 0; i < n; i += len)
{
double wx = 1, wy = 0;
for (int j = 0; j < half; j++)
{
double cx = a[i + j], cy = b[i + j];
double dx = a[i + j + half], dy = b[i + j + half];
double ex = dx * wx - dy * wy, ey = dx * wy + dy * wx;
a[i + j] = cx + ex, b[i + j] = cy + ey;
a[i + j + half] = cx - ex, b[i + j + half] = cy - ey;
double wnx = wx * wmx - wy * wmy, wny = wx * wmy + wy * wmx;
wx = wnx, wy = wny;
}
}
}
if (rev)
{
for (int i = 0; i < n; i++)
a[i] /= n, b[i] /= n;
}
}
int solve(int a[],int na,int b[],int nb,int ans[])
{
int len = max(na, nb), ln;
for(ln=0; L(ln)<len; ++ln);
len=L(++ln);
for (int i = 0; i < len ; ++i)
{
if (i >= na) ax[i] = 0, ay[i] =0;
else ax[i] = a[i], ay[i] = 0;
}
fft(ax, ay, len, 0);
for (int i = 0; i < len; ++i)
{
if (i >= nb) bx[i] = 0, by[i] = 0;
else bx[i] = b[i], by[i] = 0;
}
fft(bx, by, len, 0);
for (int i = 0; i < len; ++i)
{
double cx = ax[i] * bx[i] - ay[i] * by[i];
double cy = ax[i] * by[i] + ay[i] * bx[i];
ax[i] = cx, ay[i] = cy;
}
fft(ax, ay, len, 1);
for (int i = 0; i < len; ++i)
ans[i] = (int)(ax[i] + 0.5);
return len;
}
const int SIZE = 1000000;
int nub[SIZE];
int ans[SIZE];
int main()
{
int t;
int n,q,c,l;
scanf("%d",&t);
while( t-- )
{
memset(sum, 0, sizeof(sum));
scanf("%d%d",&n,&q);
l = 0;
for (int i = 1;i <= n;++i ){
scanf("%d",&nub[i]);
x1[nub[i]]++;
l = max(l ,nub[i]);
}
solve(x1, l+1, x1, l+1, sum);
l += l;
//for (int i = 1;i <= l;++i)cout <<sum[i]<<" ";
for ( int i = 1;i <= n;++i )
sum[ nub[i]*2 ] --;
ans[0] = 0;
for (int i = 1;i <= l;++i){
sum[i] >>= 1;
ans[i] = sum[i] + ans[i-1];
}
while ( q-- ){
scanf("%d",&c);
printf("%d\n",ans[c-1]);
}
}
return 0;
}