Codeforces Round #485 (Div. 1) D. Perfect Encoding

原题链接.

题目大意:

b是一堆正整数。
bi>=n ∏ b i >= n
bi ∑ b i 的最小值。

n<=101.5106 n <= 10 1.5 ∗ 10 6

题解:

首先我们得知道一个结论:
bi ∑ b i 确定的话,使 bi ∏ b i 最大一定是尽可能拆2、3,且2的个数不超过2。
证明自行感性认识。

所以这题变成了求大数的对数,底数是3。

一个好方法是预估。

n>=10k n >= 10 k ,则 logn3>=klog103 l o g 3 n >= k ∗ l o g 3 10 ,且差值不会超过 log103 l o g 3 10

那么设 y=klog103 y = ⌊ k ∗ l o g 3 10 ⌋ ,我们需要快速求出 3y 3 y 的精确值。

这里上分治FFT,FFT用long double,压4位精度不会炸,就是要不断把虚部化0,实部取整。

也可以超大NTT模数去NTT,需要黑科技处理大数相乘。

还可以上myy的FFT。

细节多,又卡时间又卡精度。

Code:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ld long double
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define ff(i, x, y) for(int i = x; i < y; i ++)
using namespace std;

const ld pi = acos(-1);

const int N = 1500005;

struct Z {
    ld x, y;
    Z (ld _x = 0, ld _y = 0) {x = _x, y = _y;}
};

Z operator +(Z a, Z b) {return Z(a.x + b.x, a.y + b.y);}
Z operator -(Z a, Z b) {return Z(a.x - b.x, a.y - b.y);}
Z operator *(Z a, Z b) {return Z(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}
Z w[N]; int tp;
void dft(Z *a, int n) {
    ff(i, 0, n) {
        int p = 0, q = i;
        fo(j, 1, tp) p = p * 2 + q % 2, q /= 2;
        if(p > i) swap(a[p], a[i]);
    }
    for(int m = 2; m <= n; m *= 2) {
        int h = m / 2;
        ff(i, 0, h) {
            Z W = w[i * (n / m)];
            for(int j = i; j < n; j += m) {
                int k = j + h;
                Z u = a[j], v = a[k] * W;
                a[j] = u + v; a[k] = u - v;
            }
        }
    }
}
void fft(Z *a, Z *b, int n) {
    fo(i, 0, n) w[i] = Z(cos(2 * pi * i / n), sin(2 * pi * i / n));
    dft(a, n); dft(b, n);
    ff(i, 0, n) a[i] = a[i] * b[i];
    fo(i, 0, n / 2) swap(w[i], w[n - i]);
    dft(a, n); ff(i, 0, n) a[i].x /= n;
}
void sqr(Z *a, int n) {
    fo(i, 0, n) w[i] = Z(cos(2 * pi * i / n), sin(2 * pi * i / n));
    dft(a, n);
    ff(i, 0, n) a[i] = a[i] * a[i];
    fo(i, 0, n / 2) swap(w[i], w[n - i]);
    dft(a, n); ff(i, 0, n) a[i].x /= n;
}
void jing(Z *a, int &n) {
    ff(i, 0, n) {
        ll o = a[i].x + 0.5;
        a[i + 1].x += o / 10000;
        a[i].x = o % 10000;
    }
    if(a[n].x > 0.5) n ++;
    ff(i, 0, n) a[i].y = 0, a[i].x = (ll) (a[i].x + 0.5);
}

char s[N]; int len, num[N];

int n, m, t, n0; Z a[N], b[N], c[N], d[N];

int pd(Z *a, Z *b, int n, int m) {
    if(n > m) return 1;
    if(n < m) return 0;
    fd(i, n - 1, 0) {
        int x = a[i].x + 0.5, y = b[i].x + 0.5;
        if(x > y) return 1;
        if(x < y) return 0;
    }
    return 2;
}

int main() {
    scanf("%s", s + 1); len = strlen(s + 1);
    fo(i, 1, len / 2) swap(s[i], s[len - i + 1]);
    fo(i, 5, len) num[i] = num[i - 1] + (i % 4 == 1);
    n = num[len] + 1; fd(i, len, 1) a[num[i]] = a[num[i]] * 10 + s[i] - '0';

    if(n == 1) {
        int x = a[0].x + 0.5;
        if(x == 1) {printf("1\n"); return 0;}
        if(x == 2) {printf("2\n"); return 0;}
        if(x == 3) {printf("3\n"); return 0;}
        if(x == 4) {printf("4\n"); return 0;}
    }

    int y = log(10) / log(3) * (len - 1), y0 = y;

    m = 1; b[0] = 3;
    t = 1; d[0] = 1;

    for(; y; y /= 2) {
        if(y & 1) {
            n0 = t + m - 1;
            tp = 0; while(1 << ++ tp < n0);
            n0 = 1 << tp;
            ff(i, t, n0) d[i] = (0, 0);
            ff(i, m, n0) b[i] = (0, 0);
            ff(i, 0, n0) c[i] = b[i];
            fft(d, c, n0);
            t = t + m - 1; jing(d, t);
        }
        n0 = m + m - 1;
        ff(i, m, n0) b[i] = (0, 0);
        tp = 0; while(1 << ++ tp < n0);
        n0 = 1 << tp; sqr(b, n0);
        m = m + m - 1; jing(b, m);
    }

    memset(c, 0, sizeof c);
    while(1) {
        ff(i, 0, t) c[i] = d[i];

        m = t;
        ff(i, 0, m) c[i].x *= 3;
        jing(c, m);

        int pp = pd(c, a, m, n);

        if(pp == 0) {
            ff(i, 0, m) d[i] = c[i];
            t = m;
            y0 ++;
            continue;
        }
        break;
    }

    ff(i, 0, t) c[i] = d[i]; ff(i, t, 1e6) c[i] = 0;
    int yu = 0; m = t;
    fd(i, m - 1, 0) {
        c[i].x += yu * 10000;
        yu = (int) (c[i].x + 0.5) % 3;
        c[i].x = (int) (c[i].x + 0.5) / 3;
    }
    ff(i, 0, m) c[i].x *= 4; jing(c, m);
    if(pd(c, a, m, n)) {
        printf("%d\n", y0 * 3 + 1);
        return 0;
    }


    ff(i, 0, t) c[i] = d[i]; ff(i, t, 1e6) c[i] = 0;
    m = t;
    ff(i, 0, m) c[i].x *= 2;
    jing(c, m);
    if(pd(c, a, m, n)) {
        printf("%d\n", y0 * 3 + 2);
        return 0;
    }

    printf("%d\n", (y0 + 1) * 3);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值