P3338-[ZJOI2014]力【FFT】

正题

题目链接:https://www.luogu.com.cn/problem/P3338


题目大意

F j = ∑ i = 1 j − 1 q i ∗ q j ( i − j ) 2 − ∑ i = j + 1 n q i ∗ q j ( i − j ) 2 F_j=\sum_{i=1}^{j-1}\frac{q_i*q_j}{(i-j)^2}-\sum_{i=j+1}^n\frac{q_i*q_j}{(i-j)^2} Fj=i=1j1(ij)2qiqji=j+1n(ij)2qiqj
E j = F j q j E_j=\frac{F_j}{q_j} Ej=qjFj


解题思路

显然有 F j = ∑ i = 1 j − 1 q i ( i − j ) 2 − ∑ i = j + 1 n q i ( i − j ) 2 F_j=\sum_{i=1}^{j-1}\frac{q_i}{(i-j)^2}-\sum_{i=j+1}^n\frac{q_i}{(i-j)^2} Fj=i=1j1(ij)2qii=j+1n(ij)2qi
然后定义 a i = q i , b i = 1 i 2 a_i=q_i,b_i=\frac{1}{i^2} ai=qi,bi=i21,那么有
F j = ∑ i = 1 j − 1 a i ∗ b j − i − ∑ i = j + 1 n a i ∗ b i − j F_j=\sum_{i=1}^{j-1}a_i*b_{j-i}-\sum_{i=j+1}^{n}a_i*b_{i-j} Fj=i=1j1aibjii=j+1naibij
前面那个式子显然可以卷积,然后后面那个式子把 a a a数组翻转之后就和前面那个一样了。

然后 F F T FFT FFT优化即可,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

才发现有 c o m p l e x complex complex这个库,终于不用手写复数运算了(不知道比赛能不能用)


c o d e code code

#include
#include
#include
#include
#include
#define Pi acos(-1)
using namespace std;
typedef complex<double> comp;
const int N=3e5+10;
/*struct comp{
	double x,y;
};
complex operator +(complex a,complex b)
{return (complex){a.x+b.x,a.y+b.y};}
complex operator -(complex a,complex b)
{return (complex){a.x-b.x,a.y-b.y};}
complex operator *(complex a,complex b)
{return (complex){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}*/
int n,r[N],z;
comp a1[N],a2[N],b1[N],b2[N];
void fft(comp *f,int op){
	for(int i=0;i<n;i++)
		if(i<r[i])swap(f[i],f[r[i]]);
	for(int p=2;p<=n;p<<=1){
		int len=p>>1;
		comp tmp(cos(Pi/len),sin(Pi/len)*op);
//		printf("%lf ",tmp.real()); 
		for(int k=0;k<n;k+=p){
			comp buf(1,0);
			for(int i=k;i<k+len;i++){
				comp tt=buf*f[len+i];
				f[len+i]=f[i]-tt;
				f[i]=f[i]+tt;
				buf=buf*tmp;
			}
		}
	}
	if(op==-1)
		for(int i=0;i<n;i++)
			f[i]/=n;
	return;
}
int main()
{
	scanf("%d",&n);int l;z=n;
	for(int i=1;i<=n;i++){
		double x;
		scanf("%lf",&x);
		a1[i]=a2[n-i+1]=x;
		b1[i]=b2[i]=(double)1.0/i/i;
	}
	for(l=1;l<=n*2;l<<=1);n=l;
	for(int i=0;i<n;i++)
		r[i]=(r[i>>1]>>1)|((i&1)?n>>1:0);
	fft(a1,1);fft(b1,1);
	for(int i=0;i<n;i++)
		a1[i]=a1[i]*b1[i];
	fft(a1,-1);
	fft(a2,1);fft(b2,1);
	for(int i=0;i<n;i++)
		a2[i]=a2[i]*b2[i];
	fft(a2,-1);
	for(int i=1;i<=z;i++)
		printf("%.3lf\n",a1[i].real()-a2[z-i+1].real());
	return 0;
}

你可能感兴趣的:(数论and数学)