码迷,mamicode.com
首页 > 其他好文 > 详细

[CQOI 2018]九连环

时间:2018-05-04 12:04:16      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:com   class   应该   一个   scan   i++   n+1   mat   oid   

Description

题库链接

给你一个 \(n\) 连环,游戏规则是:

  1. 第一个(最右边)环任何时候都可以任意装上或卸下;
  2. 如果第 \(k\) 个环没有被卸下,且第 \(k\) 个环右边的所有环都被卸下,则第 \(k+1\) 个环(第 \(k\) 个环左边相邻的环)可以任意装上或卸下。

现在 \(m\) 组询问,每组询问给你 \(n\) 连环,问你至少多少步取下所有的环。

\(1\leq n\leq 10^5,1\leq m\leq 10\)

Solution

数学书上推导很清楚了:

技术分享图片

技术分享图片

值得注意的是第二张图片中的 \(n\) 为奇数的推导式中 \(\frac{2(1-2^{n+1})}{1-2^2}\) 应该是 \(\frac{1-2^{n+1}}{1-2^2}\)

有幸能指出数学书的错误。

然后 \(\text{FFT}\) 快速幂乱搞就好了。不开 \(-O2\)\(\text{FFT}\) 不就是在玩火吗???

Code

这个瓜皮代码常数过大在 b 站上过不了。

#include <bits/stdc++.h>
#define dob complex<double>
using namespace std;
const int N = (100000<<2)+5;
const double pi = acos(-1.);

int n, nn, m, len, L, R[N], A[N];
dob a[N], b[N];

void FFT(dob *A, int o) {
    for (int i = 0; i < len; i++) if (i < R[i]) swap(A[i], A[R[i]]);
    for (int i = 1; i < len; i <<= 1) {
        dob wn(cos(pi/i), sin(pi*o/i)), x, y;
        for (int j = 0; j < len; j += (i<<1)) {
            dob w(1, 0);
            for (int k = 0; k < i; k++, w = w*wn) {
                x = A[j+k], y = w*A[i+j+k];
                A[j+k] = x+y, A[i+j+k] = x-y;
            }
        }
    }
}
void work() {
    scanf("%d", &n); nn = n; ++n; m = log(2)*n+5;
    for (L = 0, len = 1; len <= m; len <<= 1) ++L;
    for (int i = 0; i < len; i++) a[i] = b[i] = 0;
    for (int i = 0; i < len; i++) R[i] = (R[i>>1]>>1)|((i&1)<<(L-1));
    a[0] = 1, b[0] = 2;
    while (n) {
        FFT(a, 1), FFT(b, 1);
        if (n&1) for (int i = 0; i <= len; i++) a[i] = a[i]*b[i];
        for (int i = 0; i <= len; i++) b[i] = b[i]*b[i]; n >>= 1;
        FFT(a, -1); FFT(b, -1);
        for (int i = 0; i < len; i++) A[i] = a[i].real()/len+0.5;
        int loc = 0; while (A[loc] && loc < len) A[loc+1] += A[loc]/10, A[loc] %= 10, ++loc;
        for (int i = 0; i < len; i++) a[i] = A[i];
        for (int i = 0; i < len; i++) A[i] = b[i].real()/len+0.5;
        loc = 0; while (A[loc] && loc < len) A[loc+1] += A[loc]/10, A[loc] %= 10, ++loc;
        for (int i = 0; i < len; i++) b[i] = A[i];
    }
    for (int i = 0; i < len; i++) A[i] = a[i].real();
    A[0] -= 1+(!(nn&1));
    for (int i = len-1, flag = 0, sum = 0; i >= 0; i--) {
        sum = sum*10+A[i]; if (sum/3) flag = 1;
        if (flag) printf("%d", sum/3), sum %= 3;
    }
    puts("");
}
int main() {int t; cin >> t; while (t--) work(); return 0; }

[CQOI 2018]九连环

标签:com   class   应该   一个   scan   i++   n+1   mat   oid   

原文地址:https://www.cnblogs.com/NaVi-Awson/p/8989286.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!