【洛谷/P3338】力

测评链接

题目大意:

给定$n$个数$q_1,q_2,…q_n$,定义:

对$1\leq i \leq n$,求$E_i$的值。

数据范围

对于$100\%$的数据,$1 \leq n \leq 10^5,0<q_i<10^9$。

解题过程:

根据题意:

观察到这个式子有大量重复元素,那么不妨设出来方便表示:

即:

考虑将式子化为卷积的形式(之后解释):

卷积:形如$\sum^{n-1}_1{F_i*G_{n-i}}$的式子

显然原式已有一部分为卷积形式,那么考虑另一部分。

考虑将序列倒转,倒转后的序列满足$C_i=A_{n-i+1}$,则原式:

可化为卷积:

最终得到的就是:

算法优化:

现在这个算法的时间复杂度仍是$\Theta(n^2)$,似乎与暴力没什么区别。

但是长度连续的一串卷积除了一个个枚举外,还可以用FFT加以优化。

假设要求卷积$\sum^{n-1}_{i=1}{a_i*b_{n-i}}$(我喜欢称为长度为$n-1$的卷积)

那么构造两个多项式:

那么将两个式子相乘前n项的系数分别对应长度为$1$~$n$的卷积的值。

那么上面那个卷积的值即为$A_n*B_n$的第$n-1$项。

此时时间复杂度降为$\Theta(n\log_2n)$。

回到题目,设:

则$E_i=L_{i-1}-R_{n-i}$。

上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
const double pi=acos(-1);
inline int read()
{
int kkk=0,x=1;
char c=getchar();
while((c<'0' || c>'9') && c!='-')
c=getchar();
if(c=='-')
c=getchar(),x=-1;
while(c>='0' && c<='9')
kkk=(kkk<<3)+(kkk<<1)+(c-'0'),c=getchar();
return kkk*x;
}
struct complex
{
double x,y; //x+yi
complex(double xx=0,double yy=0){x=xx,y=yy;}
}a[300001],b[300001],c[300001];
complex operator +(complex x,complex y){return complex(x.x+y.x,x.y+y.y);}
complex operator -(complex x,complex y){return complex(x.x-y.x,x.y-y.y);}
complex operator *(complex x,complex y){return complex(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);}
int n,lim,LOG,zone[300001];
inline void FFT(complex *A,int type)
{
for(register int i=0;i<lim;++i)
if(zone[i]>i)
swap(A[i],A[zone[i]]);
for(register int len=1;len<lim;len*=2)
{
complex wn(cos(pi/len),type*sin(pi/len));
for(register int bj=0;bj<lim;bj+=2*len)
{
complex w(1,0);
for(register int i=0;i<len;++i,w=w*wn)
{
complex A0=A[bj+i],A1=w*A[bj+i+len];
A[bj+i]=A0+A1;
A[bj+i+len]=A0-A1;
}
}
}
}
int main()
{
n=read();
LOG=ceil(log2(n*2));
lim=(1<<LOG);
for(register int i=0;i<lim;++i)
zone[i]=((zone[i>>1]>>1) | ((i&1)<<(LOG-1)));
for(register int i=0;i<n;++i) //FFT习惯从0开始,结果留了一堆关于下标的烂摊子到后面
scanf("%lf",&a[i].x),c[n-i-1].x=a[i].x;
for(register int i=1;i<=n;++i)
b[i-1].x=(double)1.0/i/i;
FFT(a,1);
FFT(b,1);
FFT(c,1);
for(register int i=0;i<lim;++i)
{
a[i]=a[i]*b[i];
c[i]=c[i]*b[i];
}
FFT(a,-1);
FFT(c,-1);
for(register int i=0;i<n;++i) //这里处理得就很别扭
{
double ans=0;
if(i!=0)
ans+=a[i-1].x/lim;
if(i!=n-1)
ans-=c[n-i-2].x/lim;
printf("%.3lf\n",ans);
}
return 0;
}

------------------本文结束,感谢您的阅读------------------