首页 > 其他分享 >关于三次多项式复合的一个注记

关于三次多项式复合的一个注记

时间:2023-09-19 17:57:52浏览次数:28  
标签:return val int 多项式 注记 复合 vector const mod

首先根据熟知的变换, 复合 \(f(ax^3+bx^2+cx+d)\) 的问题的困难内核在于 \(f(x^3+cx)\), 在域上, 只要解决某个 \(c\neq 0\) 的情况, 就解决了一般的情况.

取 \(c = -3\), 我们有

\[x^3 - 3x = (x^3 + x^{-3}) \circ (x + x^{-1})^{\langle-1\rangle}. \]

于是问题的难点转换成了计算复合 \(f(x+x^{-1})\), 由于

\[f\left(\frac{x+x^{-1}}2\right) = f\left( \frac{x+1}{x-1} \right) \circ x^2 \circ \frac{x+1}{x-1}, \]

整个问题可以在 \(O(n\log n)\) 时间内解决.

之前本来想要出题, 但是做过一些实验发现有几十倍的常数, 很难击败 \(O(n\log^2 n)\) 做法.

由于某些原因, 今天公开一下上面的注记.

这是本来想要出的交互题的代码:

// polycomp.h
namespace __athekatelan__ {
    typedef unsigned long long u64;

    const int mod = 998244353;
    extern long long op_cnt, mul_sum;

    class field {
        int val;
    public:
        field(int val = 0) : val(val) {}
        inline field operator+(const field& rhs) const {
            ++op_cnt;
            int nval = val + rhs.val;
            if (nval >= mod) nval -= mod;
            return nval;
        }
        inline field operator-(const field& rhs) const {
            ++op_cnt;
            int nval = val - rhs.val;
            if (nval < 0) nval += mod;
            return nval;
        }
        inline field operator*(const field& rhs) const {
            ++op_cnt;
            return val * (u64)rhs.val % mod;
        }
        // Participant should not access this
        inline int __get() const {
            return val;
        }
    };

    std::vector<field> mul(const std::vector<field>& a, const std::vector<field>& b);
}

using __athekatelan__::mod;
using __athekatelan__::field;

// std.cpp

int mpow(int x, int k) {
    if (k == 0) return 1;
    int ret = mpow(x * (u64)x % mod, k >> 1);
    if (k & 1) ret = ret * (u64)x % mod;
    return ret;
}
int inv(int a) { return mpow(a, mod - 2); }

int fac[1 << 21], ifac[1 << 21];

void prepare(int n) {
    fac[0] = 1;
    for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * (u64)i % mod;
    ifac[n] = inv(fac[n]);
    for (int i = n; i; --i) ifac[i - 1] = ifac[i] * (u64)i % mod;
}

vector<field> taylor(vector<field> f, int a) {
    int n = f.size() - 1;
    for (int i = 0; i <= n; ++i) f[i] = f[i] * fac[i];
    reverse(f.begin(), f.end());
    vector<field> op(n + 1);
    int pw = 1;
    for (int i = 0; i <= n; ++i) {
        op[i] = pw * (u64)ifac[i] % mod;
        pw = pw * (u64)a % mod;
    }
    f = mul(f, op);
    f.resize(n + 1);
    reverse(f.begin(), f.end());
    for (int i = 0; i <= n; ++i) f[i] = f[i] * ifac[i];
    return f;
}

vector<field> comp_q(vector<field> f, int q) {
    int pw = 1;
    for (int i = 0; i != f.size(); ++i) {
        f[i] = f[i] * pw;
        pw = pw * (u64)q % mod;
    }
    return f;
}

vector<field> comp_x(vector<field> f, int k) {
    int n = f.size() - 1;
    vector<field> ret(n * k + 1);
    for (int i = 0; i <= n; ++i) ret[i * k] = f[i];
    return ret;
}

vector<field> mobius(vector<field> f) {
    reverse(f.begin(), f.end());
    f = taylor(comp_q(f, mod - 2), mod - inv(2));
    reverse(f.begin(), f.end());
    return taylor(f, 1);
}

vector<field> solve_3(vector<field> f) {
    int n = f.size() - 1;
    f = mobius(comp_x(mobius(comp_q(f, 2)), 2));
    int val = mpow(4, mod - 1 - n);
    for (int i = 0; i <= n * 2; ++i) f[i] = f[i] * val;
    f = comp_x(f, 3);
    f = mobius(f);
    vector<field> g(n * 3 + 1);
    val = mpow(8, mod - 1 - n);
    for (int i = 0; i <= n * 3; ++i) g[i] = f[i * 2] * val;;
    return comp_q(mobius(g), inv(2));
}

vector<field> solve_a(vector<field> f, int c) {
    int n = f.size() - 1;
    if (c == 0) return comp_x(f, 3);
    int l2 = c * (u64)inv(mod - 3) % mod;
    vector<field> even(n + 1), odd(n + 1);
    {
        int q = l2 * (u64)l2 % mod * l2 % mod, pw = 1;
        for (int i = 0; i <= n; i += 2) {
            even[i] = f[i] * pw;
            if (i + 1 <= n)
                odd[i + 1] = f[i + 1] * (pw * (u64)l2 % mod);
            pw = pw * (u64)q % mod;
        }
    }
    even = solve_3(even); odd = solve_3(odd);
    int q = inv(l2), pw = 1;
    for (int i = 0; i <= n * 3; i += 2) {
        even[i] = even[i] * pw;
        if (i + 1 <= n * 3)
            even[i + 1] = odd[i + 1] * pw;
        pw = pw * (u64)q % mod;
    }
    return even;
}

vector<field> comp(vector<field> f, vector<int> g) {
    int n = f.size() - 1, m = g.size() - 1;
    prepare(n * 6);
    {
        int a = g.back(), ia = inv(a);
        for (int i = 0; i <= m; ++i) g[i] = g[i] * (u64)ia % mod;
        f = comp_q(f, a);
    }
    if (m == 1) return taylor(f, g[0]);
    else if (m == 2) {
        int u = (g[0] + (mod - 1) / 4ull * g[1] % mod * g[1]) % mod;
        f = taylor(f, u);
        vector<field> db(n * 2 + 1);
        for (int i = 0; i <= n; ++i) db[i * 2] = f[i];
        return taylor(db, (mod - (mod - 1) / 2ull) * g[1] % mod);
    } else {
        int u = inv(3) * (u64)g[2] % mod;
        int r = (mod - u) % mod, r2 = r * (u64)r % mod, r3 = r2 * (u64)r % mod;
        g[0] = (g[0] + g[1] * (u64)r + g[2] * (u64)r2 + r3) % mod;
        g[1] = (g[1] + g[2] * 2ull * r + 3ull * r2) % mod;
        g[2] = (g[2] + 3ull * r) % mod;
        return taylor(solve_a(taylor(f, g[0]), g[1]), u);
    }
}

标签:return,val,int,多项式,注记,复合,vector,const,mod
From: https://www.cnblogs.com/Elegia/p/composite-cubic.html

相关文章

  • MySQL中的一些复合数据类型
    ENUM枚举类型ENUM适合于只能在一组固定值中选一个的场景,比如性别只能为男或者女。ENUM的优势在于:只能在固定值中选择,可以在数据库层面限制非法值。数据的存储用数字来存储,占用空间少。但是它的使用有很多需要我们注意的地方,一不小心就会得到错误的结果。createtabletes......
  • 复合索引 multiple-column index Composite Indexes
    MySQL::MySQL8.0ReferenceManual::8.3.6Multiple-ColumnIndexeshttps://dev.mysql.com/doc/refman/8.0/en/multiple-column-indexes.html MySQL::MySQL8.0ReferenceManual::8.2.1.3IndexMergeOptimizationhttps://dev.mysql.com/doc/refman/8.0/en/inde......
  • 3.9 Java位运算符:Java移位运算符、复合位赋值运算符及位逻辑运算符
    Java 定义的位运算(bitwiseoperators)直接对整数类型的位进行操作,这些整数类型包括long,int,short,char和byte。位运算符主要用来对操作数二进制的位进行运算。按位运算表示按每个二进制位(bit)进行计算,其操作数和运算结果都是整型值。Java语言中的位运算符分为位逻辑运算符和位移......
  • 可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫
    P8946TheLostSymbol这种类型的dp的特点就是大部分转移形如\(f(i,j)\rightarrowf(i+1,j+1)\)之类的,并且当以上转移出现时原数组被清空,这就可以用一个deque来维护,然后对于全局赋值/全局加,需要对每个位置维护一个时间戳,并记录上一次赋值/加是什么时候,以便标记下传。(貌似......
  • 快速傅里叶变换计算多项式乘法
    前言OI中,多项式有着十分广泛的应用。其基础是多项式的基本运算,几乎所有多项式运算都是由多项式加法和乘法拼接成的。我们有显然的\(O(n)\)的办法计算多项式加法,而朴素的多项式乘法是很多情况下难以接受的\(O(n^2)\)的复杂度。快速傅里叶变换(FFT)可以高效(\(O(n\logn)\))计算多......
  • 多项式模板
    总算把之前摸鱼多项式欠下的东西还清了些。。。常数应该不算特别大点击查看代码namespacePolys{#definePolystd::vector<int>#definelllonglongconstintG=3,MOD=998244353;llpower(lla,llb=MOD-2){llret=1;......
  • 多项式全家桶(未全)
    一些约定:下面\(f^i(x)\)表示\(i\)阶导数。\(f(x)^i\)表示幂次。若不说明绝大部分除一个多项式时都是代表乘上它的逆。多项式加减,求导积分过于简单不讲。\((x^a)'=ax^{a-1},\displaystyle\intx^a{\rmd}x=\dfrac{x^{a+1}}{a+1}+C\)。实际操作时\(C=0\),正确性是好证的。(如......
  • 多项式ln
    给出\(n-1\)次多项式\(F(x)\),求一个\(\bmod{\:x^n}\)下的多项式\(B(x)\),满足\(g(x)\equiv\lnf(x)\)(\(f_0=1\))。\[g'(x)=\ln'(f(x))\timesf'(x)=\frac{f'(x)}{f(x)}(\bmodx^n)\]求导,求逆,求积即可。LLTmp[N];voidPolyInv(LL*a,LL*I,LL......
  • 18、复合类型之指针(P47、P48、P49、P50);C++ primer 2.3.2
    1、C++中的“声明符”是什么?声明符是用来指定变量或函数的类型、名称和属性的符号。例如:intlist[20]; 声明了一个名为list的整型数组,它有20个元素。int是类型说明符,list[20]是声明符char*cp; 声明了一个名为cp的指向字符的指针1。*cp是声明符doublefunc(void);......
  • 17、复合类型之引用(P45、P46)
     P45倒数第五行“引用为对象起了另外一个名字,引用类型引用另外一种类型。”intx=10;//声明一个整数变量x,并初始化为10int&refX=x;//声明一个整数引用refX,它是x的别名ref就是x的另外一个名字,refX就是引用类型,它引用了x(int整形)P45倒数第四行,声明符:具体来说,声......