【BZOJ3527】[ZJOI2014] 力(FFT)

题目:

BZOJ3527

分析:

FFT应用第一题……

首先很明显能把 F j F_j Fj约掉,变成:

E j = ∑ i &lt; j q i ( i − j ) 2 − ∑ i &gt; j q i ( i − j ) 2 E_j=\sum _{i&lt;j} \frac{q_i}{(i-j)^2}-\sum_{i&gt;j}\frac{q_i}{(i-j)^2} Ej=i<j(ij)2qii>j(ij)2qi

然后去膜拜题解,我们知道两个多项式相乘的方式如下:

C j = ∑ i = 0 j A i B j − i C_j=\sum_{i=0}^j A_iB_{j-i} Cj=i=0jAiBji

那么,如果把 E j E_j Ej的表达式化成上面那个形式,就可以用FFT计算了。(不会FFT?戳我:【知识总结】快速傅里叶变换(FFT)

先看减号前面的部分。显然可以变成(为了叙述方便,读入的 q q q的下标为 [ 0 , n ) [0,n) [0,n)):

C j = ∑ i = 0 j F i G j − i C_j=\sum_{i=0}^j F_iG_{j-i} Cj=i=0jFiGji

其中 F i = q i F_i=q_i Fi=qi G i = 1 i 2 G_i=\frac{1}{i^2} Gi=i21。特别地, G 0 = 0 G_0=0 G0=0

减号后面要处理 j j j位置以后的,怎么办?大力把 q q q数组翻过来,这样就相当于求 n − j − 1 n-j-1 nj1以前的了:

D j = ∑ i = 0 j F i ′ G j − i D_j=\sum_{i=0}^{j} F&#x27;_iG_{j-i} Dj=i=0jFiGji

其中 F j ′ = q n − j − 1 F&#x27;_j=q_{n-j-1} Fj=qnj1

那么答案 E j = C j − D n − j − 1 E_j=C_j-D_{n-j-1} Ej=CjDnj1

代码:

注意 g g g要初始化……

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cmath>
using namespace std;
#define _ 0
 
namespace zyt
{
	template<typename T>
	inline void read(T &x)
	{
		char c;
		bool f = false;
		x = 0;
		do
			c = getchar();
		while (c != '-' && !isdigit(c));
		if (c == '-')
			f = true, c = getchar();
		do
			x = x * 10 + c - '0', c = getchar();
		while (isdigit(c));
		if (f)
			x = -x;
	}
	inline void read(double &x)
	{
		scanf("%lf", &x);
	}
	template<typename T>
	inline void write(T x)
	{
		static char buf[20];
		char *pos = buf;
		if (x < 0)
			putchar('-'), x = -x;
		do
			*pos++ = x % 10 + '0';
		while (x /= 10);
		while (pos > buf)
			putchar(*--pos);
	}
	inline void write(const double a, const int fix = 9)
	{
		printf("%.*f", fix, a);
	}
	const int B = 17, N = 1 << (B + 1) | 10;
	const double PI = 3.141592653589793238462643383279502884197169399375105820974944L;
	namespace FFT
	{
		int rev[N];
		struct cpx
		{
			double x, y;
			cpx(const double _x = 0.0, const double _y = 0.0)
				: x(_x), y(_y) {}
			cpx operator + (const cpx &b) const
			{
				return cpx(x + b.x, y + b.y);
			}
			cpx operator - (const cpx &b) const
			{
				return cpx(x - b.x, y - b.y);
			}
			cpx operator * (const cpx &b) const
			{
				return cpx(x * b.x - y * b.y, x * b.y + y * b.x);
			}
			cpx conj() const
			{
				return cpx(x, -y);
			}
		}omega[N], inv[N];
		void init(const int lg2)
		{
			for (int i = 0; i < (1 << lg2); i++)
			{
				rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg2 - 1));
				omega[i] = cpx(cos(2 * PI * i / (1 << lg2)), sin(2 * PI * i / (1 << lg2)));
				inv[i] = omega[i].conj();
			}
		}
		void fft(cpx *a, const int n, const cpx *w)
		{
			for (int i = 0; i < n; i++)
				if (i < rev[i])
					swap(a[i], a[rev[i]]);
			for (int len = 1; len < n; len <<= 1)
				for (int i = 0; i < n; i += (len << 1))
					for (int k = 0; k < len; k++)
					{
						cpx tmp = a[i + k] - w[k * (n / (len << 1))] * a[i + len + k];
						a[i + k] = a[i + k] + w[k * (n / (len << 1))] * a[i + len + k];
						a[i + len + k] = tmp;
					}
		}
		void solve(cpx *a, cpx *b, const int n)
		{
			fft(a, n, omega), fft(b, n, omega);
			for (int i = 0; i < n; i++)
				a[i] = a[i] * b[i];
			fft(a, n, inv);
			for (int i = 0; i < n; i++)
				a[i].x /= n;
		}
	}
	using namespace FFT;
	int n;
	double q[N];
	cpx f[N], g[N], revf[N];
	int work()
	{
		read(n);
		for (int i = 0; i < n; i++)
		{
			read(q[i]);
			f[i] = revf[n - i - 1] = q[i];
		}
		int m = n << 1, lg2 = 0;
		for (n = 1; n < m; n <<= 1)
			++lg2;
		init(lg2);
		for (int i = 0; i < (m >> 1); i++)
			g[i] = (i ? 1.0 / ((double)i * i) : 0.0);
		solve(f, g, n);
		for (int i = 0; i < (m >> 1); i++)
			g[i] = (i ? 1.0 / ((double)i * i) : 0.0);
		for (int i = (m >> 1); i < n; i++)
			g[i] = 0;
		solve(revf, g, n);
		for (int i = 0; i < (m >> 1); i++)
			write(f[i].x - revf[(m >> 1) - i - 1].x), putchar('\n');
		return ~~(0^_^0);
	}
}
 
int main()
{
	return zyt::work();	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值