FFT(Fast Fourier Transformation)就是“快速傅里叶变换”的意思,它是一种用来计算DFT(离散傅里叶变换)和IDFT(离散傅里叶反变换)的一种快速算法。这种算法运用了一种高深的数学方式、把原来复杂度为O(n2)的朴素多项式乘法转化为了O(nlogn)的算法。
FFT能将多项式乘法O(n^2) 优化为 O(nlogn)
但是题目中并不是直接了当的求多项式乘法,而是将问题转化成多项式乘法的形式。
众所周知高精度乘法其实是两个字符串模拟乘法,而两个字符串可以看成两个多项式,满足多项式乘法的性质。
可以使用FFT加速
#include
using namespace std;
const double pi = acos(-1.0);
// 复数结构体
struct Complex {
double x,y;
Complex(double _x = 0.0,double _y = 0.0) {
x = _x;
y = _y;
}
Complex operator - (const Complex &b)const {
return Complex(x-b.x,y-b.y);
}
Complex operator + (const Complex &b)const {
return Complex(x+b.x,y+b.y);
}
Complex operator * (const Complex &b)const {
return Complex(x*b.x-y*b.y,x*b.y+y*b.x);
}
};
/* 进行FFT和IFFT前的反转变化
* 位置 i 和 (i 二进制反转后位置) 互换
* len 必须为 2 的整数幂
*/
void change(Complex y[],int len) {
int i,j,k;
for(i=1,j=len/2;i= k) {
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* FFT
* len必须是2^k形式
* on = 1是FFT, -1是IFFT
*/
void fft(Complex y[],int len,int on) {
change(y,len);
for(int h=2;h<=len;h<<=1) {
Complex wn(cos(-on*2*pi/h),sin(-on*2*pi/h));
for(int j=0;j0) len--;
for(int i=len;i>=0;i--)
printf("%c",sum[i]+'0');
printf("\n");
}
return 0;
}
题意
题目是给了n条线段。问随机取三个,可以组成三角形的概率。
首先题目给了a数组,
如样例一:
4
1 3 3 4
把这个数组转化成num数组,num[i]表示长度为i的有num[i]条。
样例一就是
num = {0 1 0 2 1}
代表长度0的有0根,长度为1的有1根,长度为2的有0根,长度为3的有两根,长度为4的有1根。
使用FFT解决的问题就是num数组和num数组卷积。
num数组和num数组卷积的解决,其实就是从{1 3 3 4}取一个数,从{1 3 3 4}再取一个数,他们的和每个值各有多少个
例如{0 1 0 2 1}*{0 1 0 2 1} 卷积的结果应该是{0 0 1 0 4 2 4 4 1 }
长度为n的数组和长度为m的数组卷积,结果是长度为n+m-1的数组。
{0 1 0 2 1}*{0 1 0 2 1} 卷积的结果应该是{0 0 1 0 4 2 4 4 1 }。
这个结果的意义如下:
从{1 3 3 4}取一个数,从{1 3 3 4}再取一个数
取两个数和为 2 的取法是一种:1+1
和为 4 的取法有四种:1+3, 1+3 ,3+1 ,3+1
和为 5 的取法有两种:1+4 ,4+1;
和为 6的取法有四种:3+3,3+3,3+3,3+3,3+3
和为 7 的取法有四种: 3+4,3+4,4+3,4+3
和为 8 的取法有 一种:4+4
利用FFT可以快速求取循环卷积,具体求解过程不解释了,就是DFT和FFT的基本理论了。
总之FFT就是快速求到了num和num卷积的结果。只要长度满足>=n+m+1.那么就可以用循环卷积得到线性卷积了。
弄完FFT得到一个num数组,这个数组的含义在上面解释过了。
while(len < 2*len1) len <<= 1;
for(int i=0;i
这里代码中的num数组就是卷积后的结果,表示两两组合。
但是题目中本身和本身组合是不行的,所有把取同一个的组合的情况删掉。
len = 2*a[n-1];
for(int i=0;i
还有,这个问题求组合,所以第一个选t1,第二个选t2,和第一个选t2,第二个选t1,我们认为是一样的。
所有num数组整体除于2
for(int i=1;i<=len;i++) num[i]/=2;
然后对num数组求前缀和
sum[0] = 0;
for(int i=1;i<=len;i++) sum[i] = sum[i-1]+num[i];
之后就开始O(n)找可以形成三角形的组合了。
a数组从小到大排好序。
对于a[i]. 我们假设a[i]是形成的三角形中最长的。这样就是在其余中选择两个和>a[i],而且长度不能大于a[i]的。(注意这里所谓的大于小于,不是说长度的大于小于,其实是排好序以后的,位置关系,这样就可以不用管长度相等的情况,排在a[i]前的就是小于的,后面的就是大于的)。
根据前面求得的结果。
长度和大于a[i]的取两个的取法是sum[len]-sum[a[i]].
但是这里面有不符合的。
一个是包含了取一大一小的
cnt -= (long long)(n-1-i)*i;
一个是包含了取一个本身i,然后取其它的
cnt -= (n-1);
还有就是取两个都大于的了
cnt -= (long long)(n-1-i)*(n-i-2)/2;
这样把i从0~n-1累加,就答案了。
ll cnt = 0;
for(int i=0;i
#include
using namespace std;
const double pi = acos(-1.0);
struct Complex {
double x,y;
Complex(double _x = 0.0,double _y = 0.0) {
x = _x;
y = _y;
}
Complex operator - (const Complex &b)const {
return Complex(x-b.x,y-b.y);
}
Complex operator + (const Complex &b)const {
return Complex(x+b.x,y+b.y);
}
Complex operator * (const Complex &b)const {
return Complex(x*b.x-y*b.y,x*b.y+y*b.x);
}
};
void change(Complex y[],int len) {
int i,j,k;
for(i=1,j=len/2;i= k) {
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
void fft(Complex y[],int len,int on) {
change(y,len);
for(int h=2;h<=len;h<<=1) {
Complex wn(cos(-on*2*pi/h),sin(-on*2*pi/h));
for(int j=0;j
题目
题解
代码
/**
* 给出n个数a1,a2,...,an,问对于每一个ak,求有多少个有序二元组(i,j)满足(ai*aj) mod P = ak
* 求出 P 的原根 g
* 上式变成 g^bi*g^bj mod P = g^bk
* bi + bj == bk
* 举个例子 p = 7,7 的原根 g = 3
* b 0 1 2 3 4 5 6 7 8 9 10 11 12
* g^bi 1 3 9 27 81 243
* g^bi mod p 1 3 2 6 4 5 1 3 2 6 4 5 1
* 我们可以观察到这里 p-1 为循环节,那么我们只需要记录前p-1个bi即可。
* 我们设I[x] = pos,表示当取模的值为x时 ,bi 为多少,如I[3] = 1,I[2] = 2,I[6] = 3;
* 那么对于输入的n个数,我们就可以知道每个数x的I[x]为多少。
* 然后再记录一下每个bi出现了多少次,用num[]记录。
* 对num[]数组做fft自我卷积得到了sum[]数组。
* sum[]数组表示sum[k] = num[i]*num[j] (k == i+j) 即和为k时的二元组的个数
* 当时需要注意的是卷积之后的k的范围为[0,2*p-3)
* 之后的[p-1,2*p-3)需要归入[0,p-1)中,因为其是循环节则我们用i%(p-1)归入即可。
*
*/
#include
using namespace std;
const double pi = acos(-1.0);
// 复数结构体
struct Complex {
double x,y;
Complex(double _x = 0.0,double _y = 0.0) {
x = _x;
y = _y;
}
Complex operator - (const Complex &b)const {
return Complex(x-b.x,y-b.y);
}
Complex operator + (const Complex &b)const {
return Complex(x+b.x,y+b.y);
}
Complex operator * (const Complex &b)const {
return Complex(x*b.x-y*b.y,x*b.y+y*b.x);
}
};
/* 进行FFT和IFFT前的反转变化
* 位置 i 和 (i 二进制反转后位置) 互换
* len 必须为 2 的整数幂
*/
void change(Complex y[],int len) {
int i,j,k;
for(i=1,j=len/2;i= k) {
j -= k;
k /= 2;
}
if(j < k) j += k;
}
}
/*
* FFT
* len必须是2^k形式
* on = 1是FFT, -1是IFFT
*/
void fft(Complex y[],int len,int on) {
change(y,len);
for(int h=2;h<=len;h<<=1) {
Complex wn(cos(-on*2*pi/h),sin(-on*2*pi/h));
for(int j=0;j>= 1;
}
return res;
}
ll getEular(ll p) {
ll ans = p;
for(ll i = 2;i*i <= p;i++) {
if(p % i == 0) {
ans -= ans / i;
while(p % i == 0) p/=i;
}
}
if(p > 1) ans -= ans / p;
return ans;
}
ll getRoot(ll p) {
ll fip = p-1;
if(p == 2) return 1;
getFactor(fip);
for(ll g=2;g= P) printf("0\n");
else {
if(brr[i]) printf("%lld\n",c[I[brr[i]]]);
else printf("%lld\n",ansz);
}
}
}
return 0;
}