[ZJOI2014]力
差卷积的实现。
库仑力?我物理但都不会。。。
$$ \begin{aligned} E_j=& \dfrac{F_j}{q_j} \\ =& \sum_{i=1}^{j-1}\dfrac{q_i}{(i-j)^2}-\sum_{i=j+1}^{n}\dfrac{q_i}{(i-j)^2} \end{aligned} $$
定义 $f_i=q_i$,$g_i=1/i^2$,特别地,令 $g_0=0$,于是我们有:
$$ \begin{aligned} E_j=& \sum _ {i=0} ^ {j} f _ i g _ {j-i} - \sum _ {i=j} ^ {n} f _ i g _ {i-j} \\ =& \sum _ {i=0} ^ {j} f _ i g _ {j-i} - \sum _ {i=0} ^ {n-j} f _ {i+j} g _ {i} \end{aligned} $$
令 $f ^ {\prime} _ i = f _ {n-i}$,则 $f _ {i} = f ^ {\prime} _ {n-i}$,于是我们有:
$$ E_j= \sum _ {i=0} ^ {j} f _ i g _ {j-i} - \sum _ {i=0} ^ {n-j} f ^ {\prime} _ {(n-j)-i} g _ {i} $$
前面和后面的卷积分开做,后面那一部分的卷积做完之后需要再翻转一次然后贡献给最终数列。
时间复杂度 $O(n\log n)$。
代码:
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 #include <iostream> #include <cstdio> #include <cmath> #define ll long long #define ld double using namespace std;namespace Ehnaev{ inline ll read () { ll ret=0 ,f=1 ;char ch=getchar (); while (ch<48 ||ch>57 ) {if (ch==45 ) f=-f;ch=getchar ();} while (ch>=48 &&ch<=57 ) {ret=(ret<<3 )+(ret<<1 )+ch-48 ;ch=getchar ();} return ret*f; } }using Ehnaev::read; const ll N=2e5 ;const ld Pi=acos (-1 );struct CP { ld x,y;inline CP (ld a=0 ,ld b=0 ) {x=a;y=b;} inline CP operator +(const CP &r)const {return CP (x+r.x,y+r.y);} inline CP operator -(const CP &r)const {return CP (x-r.x,y-r.y);} inline CP operator *(const CP &r)const {return CP (x*r.x-y*r.y,x*r.y+y*r.x);} }ans[N*2 +5 ],f[N*2 +5 ],g[N*2 +5 ],h[N*2 +5 ]; ll n,m; ll rev[N*2 +5 ]; inline void FFT (CP *f,bool op) { for (ll i=0 ;i<n;i++) if (i<rev[i]) swap (f[i],f[rev[i]]); for (ll p=2 ;p<=n;p<<=1 ) { ll len=p>>1 ;CP tG (cos(2 *Pi/(ld)p),(op?-1 :1 )*sin(2 *Pi/(ld)p)) ; for (ll k=0 ;k<n;k+=p) { CP buf (1 ,0 ) ; for (ll l=k;l<k+len;l++,buf=buf*tG) { CP t=buf*f[l+len];f[l+len]=f[l]-t;f[l]=f[l]+t; } } } if (op) {for (ll i=0 ;i<n;i++) f[i].x/=(ld)n;} } int main () { n=read (); for (ll i=1 ;i<=n;i++) { scanf ("%lf" ,&f[i].x);h[n-i].x=f[i].x;g[i].x=(ld)(1.0 /i/i); } for (m=n,n=1 ;n<=(m<<1 );n<<=1 ); for (ll i=0 ;i<n;i++) rev[i]=(rev[i>>1 ]>>1 )|((i&1 )?(n>>1 ):0 ); FFT (f,0 );FFT (g,0 );FFT (h,0 ); for (ll i=0 ;i<n;i++) {f[i]=f[i]*g[i];h[i]=h[i]*g[i];} FFT (f,1 );FFT (h,1 ); for (ll i=1 ;i<=m;i++) printf ("%.3lf\n" ,f[i].x-h[m-i].x); return 0 ; }