首页 > 其他 > 详细

两道FFT题目略解 - [ZJOI2014]力、[AH2017/HNOI2017]礼物

时间:2020-01-02 21:23:24      阅读:77      评论:0      收藏:0      [点我收藏+]

[ZJOI2014]力

Description

给出 \(n\) 个数 \(q_1,q_2, \dots q_n\),定义
\[ F_j~=~\sum_{i = 1}^{j - 1} \frac{q_i \times q_j}{(i - j)^2}~-~\sum_{i = j + 1}^{n} \frac{q_i \times q_j}{(i - j)^2} \]
\[E_i~=~\frac{F_i}{q_i}\]\(1 \leq i \leq n\),求 \(E_i\) 的值。

Solution

\[ E_j = \frac{F_j}{q_j} \=\sum_{i = 1}^{j - 1} \frac{q_i}{(j - i)^2}~-~\sum_{i = j + 1}^{n} \frac{q_i}{(i - j)^2} \]

然后,我们令
\[ f_i = \frac{1}{i!},g_i=q_i \]
\(g'_i\)表示将\(g_i\)反向后的数组,那么原式就变成了
\[ E_j =\sum_{i = 1}^{j - 1} f_{j-i}g_i - \sum_{i = 1}^{n-j}f_{j-i}g'_i \]
很显然这是一个卷积的形式,直接\(FFT\)求即可。

Code

#include <bits/stdc++.h>
using namespace std;

inline int ty() {
    char ch = getchar(); int x = 0, f = 1;
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

const int _ = 4e5 + 10;
const double Pi = acos(-1.0);
struct Complex {
    double x, y;
    Complex operator+(const Complex &b) const { return {x + b.x, y + b.y}; }
    Complex operator-(const Complex &b) const { return {x - b.x, y - b.y}; }
    Complex operator*(const Complex &b) const { return {x * b.x - y * b.y, x * b.y + y * b.x}; }
} F[_], G[_];
int N, lim = 1, pos[_];
double p[_], q[_], t[_], a[_], b[_];

void fft(int lim, Complex *a, int op) {
    for (int i = 0; i < lim; ++i)
        if (i < pos[i]) swap(a[i], a[pos[i]]);
    for (int len = 2; len <= lim; len <<= 1) {
        int mid = len >> 1;
        Complex Wn = { cos(2.0 * Pi / len), op * sin(2.0 * Pi / len) };
        for (int i = 0; i < lim; i += len) {
            Complex w = { 1, 0 };
            for (int j = 0; j < mid; ++j, w = w * Wn) {
                Complex x = a[i + j], y = w * a[i + j + mid];
                a[i + j] = x + y;
                a[i + j + mid] = x - y;
            }
        }
    }
}

void work(double *a, double *b, double *ret) {
    for (int i = 0; i < lim; ++i) {
        F[i].x = a[i], G[i].x = b[i];
        F[i].y = G[i].y = 0;
    }
    fft(lim, F, 1);
    fft(lim, G, 1);
    for (int i = 0; i < lim; ++i) F[i] = F[i] * G[i];
    fft(lim, F, -1);
    for (int i = 1; i <= N; ++i) ret[i] = F[i].x / lim;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("force.in", "r", stdin);
    freopen("force.out", "w", stdout);
#endif
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
        scanf("%lf", &p[i]);
        q[i] = p[i], t[i] = 1.0 / i / i;
    }
    reverse(q + 1, q + N + 1);
    int k = 0; while (lim <= N + N) lim <<= 1, ++k;
    for (int i = 0; i < lim; ++i) pos[i] = ((pos[i >> 1] >> 1)) | ((i & 1) << (k - 1));
    work(p, t, a);
    work(q, t, b);
    for (int i = 1; i <= N; ++i) printf("%.3lf\n", a[i] - b[N - i + 1]);
    return 0;
}

[AH2017/HNOI2017]礼物

Description

传送门

Solution

考虑给数列加上一个增量后,将式子进行展开:
\[ \sum_{i=1}^{n} (a_i-b_i+x)^2 = \\sum_{i=1}^{n}a_i^2 + \sum_{i=1}^{n}b_i^2 + nx^2 + 2x (\sum_{i=1}^n a_i - \sum_{i=1}^{n}b_i) - 2\sum_{i=1}^{n} a_i b_i \]
前面的项最小值是确定的,直接利用二次函数求解即可。

现在问题就变成了求\(\sum_{i=1}^{n} a_i b_i\)的最大值。

另外还有关于环的问题,所以只需将\(b_i\)反向,然后倍长\(a_i\),求出卷积后,取第\(n+1\)项到第\(2n\)项的最大值即可。

Code

#include <bits/stdc++.h>
using namespace std;

namespace IO {
const int bufl = 1 << 15;
char buf[bufl], *s = buf, *t = buf;
inline int fetch() {
  if (s == t) {
    t = (s = buf) + fread(buf, 1, bufl, stdin);
    if (s == t) return EOF;
  }
  return *s++;
}
inline int ty() {
  int a = 0;
  int b = 1, c = fetch();
  while (!isdigit(c)) b ^= c == '-', c = fetch();
  while (isdigit(c)) a = a * 10 + c - 48, c = fetch();
  return b ? a : -a;
}
}  // namespace IO
using IO::ty;

typedef long long ll;
const int _ = 1e6 + 10;
const double Pi = acos(-1.0);
struct Complex {
    double x, y;
    Complex operator+(const Complex &rhs) const { return { x + rhs.x, y + rhs.y }; }
    Complex operator-(const Complex &rhs) const { return { x - rhs.x, y - rhs.y }; }
    Complex operator*(const Complex &rhs) const { return { x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x }; }
} a[_], b[_];
int N, M, MX, maxx, r[_];
ll ans, suma, sumb;

inline ll _2(ll x) { return x * x; }

void fft(int lim, Complex *a, int op) {
    for (int i = 0; i < lim; ++i)
        if (i < r[i]) swap(a[i], a[r[i]]);
    for (int len = 2; len <= lim; len <<= 1) {
        int mid = len >> 1;
        Complex Wn = { cos(Pi / mid), op * sin(Pi / mid) };
        for (int i = 0; i < lim; i += len) {
            Complex w = { 1, 0 };
            for (int j = 0; j < mid; ++j, w = w * Wn) {
                Complex x = a[i + j], y = w * a[i + j + mid];
                a[i + j] = x + y;
                a[i + j + mid] = x - y;
            }
        }
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("gift.in", "r", stdin);
    freopen("gift.out", "w", stdout);
#endif
    N = ty(), MX = ty();
    for (int i = 1; i <= N; ++i) {
        a[i + N].x = a[i].x = ty();
        ans += _2((ll)a[i].x);
        suma += (ll)a[i].x;
    }
    for (int i = N; i >= 1; --i) {
        b[i].x = ty();
        ans += _2((ll)b[i].x);
        sumb += (ll)b[i].x;
    }
    M = N, N <<= 1;

    ll m1 = floor(1.0 * (sumb - suma) / N);
    ll m2 = ceil(1.0 * (sumb - suma) / N);
    ll t = suma - sumb;
    ans += min(2ll * m1 * t + M * m1 * m1, 2ll * m2 * t + M * m2 * m2);

    int lim = 1, k = 0;
    while (lim <= N + M) lim <<= 1, ++k;
    for (int i = 0; i < lim; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (k - 1));
    fft(lim, a, 1);
    fft(lim, b, 1);
    for (int i = 0; i < lim; ++i) a[i] = a[i] *  b[i];
    fft(lim, a, -1);
    for (int i = 0; i < lim; ++i) a[i].x = (ll)(a[i].x / lim + 0.5);

    ll maxx = 0;
    for (int i = M + 1; i <= M + M; ++i) maxx = max(maxx, (ll)a[i].x);
    ans -= 2 * maxx;
    printf("%lld\n", ans);
    return 0;
}

两道FFT题目略解 - [ZJOI2014]力、[AH2017/HNOI2017]礼物

原文:https://www.cnblogs.com/newbielyx/p/12141592.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!