题意:给你一个长度为N(N<=100000)的数列,Q(Q<=200000)次询问,每次给出一个K,问数列中有多少个二元组它们的和小于K
思路:显然不会做嘛,一个比较直接的想法是怎么把所有数两两相加的结果都预处理出来然后弄个前缀就OK啦?然而普通的预处理所有两个数和是要n^2的,这个时候就可以用FFT来加速啦。。
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
const double PI = acos(-1.0);
struct Complex
{
double real, image;
Complex(double _real, double _image)
{
real = _real;
image = _image;
}
Complex(){}
};
Complex operator + (const Complex &c1, const Complex &c2)
{
return Complex(c1.real + c2.real, c1.image + c2.image);
}
Complex operator - (const Complex &c1, const Complex &c2)
{
return Complex(c1.real - c2.real, c1.image - c2.image);
}
Complex operator * (const Complex &c1, const Complex &c2)
{
return Complex(c1.real*c2.real - c1.image*c2.image, c1.real*c2.image + c1.image*c2.real);
}
int rev(int id, int len)
{
int ret = 0;
for(int i = 0; (1 << i) < len; i++)
{
ret <<= 1;
if(id & (1 << i)) ret |= 1;
}
return ret;
}
Complex tA[305000];
void FFT(Complex* a, int len, int DFT)
{
for(int i = 0; i < len; i++)
tA[rev(i, len)] = a[i];
for(int s = 1; (1 << s) <= len; s++)
{
int m = (1 << s);
Complex wm = Complex(cos(DFT*2*PI/m), sin(DFT*2*PI/m));
for(int k = 0; k < len; k += m)
{
Complex w = Complex(1, 0);
for(int j = 0; j < (m >> 1); j++)
{
Complex u = w*tA[k + j + (m >> 1)];
Complex t = tA[k + j];
tA[k + j] = t + u;
tA[k + j + (m >> 1)] = t - u;
w = w*wm;
}
}
}
if(DFT == -1) for(int i = 0; i < len; i++) tA[i].real /= len, tA[i].image /= len;
for(int i = 0; i < len; i++) a[i] = tA[i];
return;
}
int branch[100010];
int num[300010];
Complex a[305000];//(1 << 17 = 131072, 1 << 18 = 262144)
lint A[200010];
lint sumA[305010];//表示A[i]的前缀和
int main()
{
int T, n;
scanf("%d", &T);
while(T--)
{
int q;
scanf("%d%d", &n,&q);
int maxBranch = 0;
for(int i = 0; i < n; i++)
{
scanf("%d", branch + i);
maxBranch = max(maxBranch, branch[i]);
}
memset(num, 0, sizeof(num));
for(int i = 0; i < n; i++)
num[branch[i]]++;
for(int i = 0; i <= maxBranch; i++)
a[i] = Complex(num[i], 0);
int len = 1;
while(len <= maxBranch) len <<= 1;
len <<= 1;
for(int i = maxBranch + 1; i < len; i++)
a[i] = Complex(0, 0);
FFT(a, len, 1);
for(int i = 0; i < len; i++)
a[i] = a[i]*a[i];
FFT(a, len, -1);
for(int i = 0; i <= 2*maxBranch; i++)
A[i] = (lint)(a[i].real + 0.5);
for(int i = 0; i <= 2*maxBranch; i += 2)
A[i] -= num[i >> 1];
for(int i = 0; i <= 2*maxBranch; i++)
A[i] /= 2;
//到现在为止A[i]表示的是去两根不同的branch的长度和为i的组合种数
sumA[0] = 0;
for(int i = 1; i <= 2*maxBranch; i++)
sumA[i] = sumA[i - 1] + A[i];
while(q--)
{
int k;
scanf("%d",&k);
printf("%lld\n",sumA[k-1]);
}
/* lint ans = 0;
sort(branch, branch + n);
for(int i = 1; i <= n; i++)//以第i根作为边最长的
{
lint tmp = sumA[2*maxBranch] - sumA[branch[i]];//另外两条边长度和要大于branch[i]
tmp -= (lint)(n - i)*(i - 1);//另外两条一条比branch[i]长, 一条不比它长
tmp -= (lint)(n - i)*(n - i - 1) / 2;//两条都比他长
tmp -= n - 1;//另外两条的组合中包括它自己的组合
ans += tmp;
}
double p = ans*6./n/(n - 1)/(n - 2);
printf("%.7f\n", p);*/
}
return 0;
}