zoukankan      html  css  js  c++  java
  • BZOJ 3456: 城市规划 与 多项式求逆算法介绍(多项式求逆, dp)

    题面

    求有 (n) 个点的无向有标号连通图个数 . ((1 le n le 1.3 * 10^5))

    题解

    首先考虑 dp ... 直接算可行的方案数 , 容易算重复 .

    我们用总方案数减去不可行的方案数就行了 (容斥)

    (f_i) 为有 (i) 个点的无向有标号连通图个数 .

    考虑 (1) 号点的联通块大小 , 联通块外的点之间边任意 但 不能与 (1) 有间接联系 .

    那么就有

    [displaystyle f_i = 2^{inom i 2} - sum_{j=1}^{i-1} f_j imes inom {i-1}{j-1} imes 2^{inom{i-j}{2}} ]

    这个可以直接用 CDQ分治FFT 求 , 但我不会 ...

    那认真考虑一下这个式子 qwq

    先展开组合数

    [displaystyle f_i=2^{inom{i}{2}} - sum_{j=1}^{i-1} f_j imes frac{(i-1)!}{(j-1)!(i-j)!} imes 2^{inom {i-j}{2}} ]

    除去 ((i-1)!)

    [displaystyle frac{f_i}{(i-1)!} = frac{2^{inom i 2}}{(i-1)!} - sum_{j=1}^{i-1} frac{f_j imes2^{inom{i-j}{2}}}{(j-1)!(i-j)!} ]

    移项合并

    [displaystyle sum_{j=1}^{i} frac{f_j imes 2^{inom {i-j}{2}}}{(j-1)! imes(i-j)!} = frac{2^{inom i 2}}{(i-1)!} ]

    左边我们观察一下不难发现是一个卷积形式 .

    (displaystyle A=sum_{i=1}^{n} frac{f_i}{(i-1)!} x^i)

    (displaystyle B=sum_{i=0}^{n} frac{2^{inom {i}{2}}}{i!} x^i)

    (displaystyle C = sum_{i=1}^{n} frac{2^{inom{i}{2}}}{(i-1)!} x^i)

    就有 (A * B = C Rightarrow A=C * B^{-1})

    我们只需要求出 (B)(x^n) 下的逆元就行了 qwq

    怎么求呢 :

    多项式求逆 :

    如果 (B)(A)(x^n) 意义下的逆元 , 那么数学表达就是 :

    [A * B equiv 1 pmod {x^n} ]

    假设我们已经求出了(B)(displaystyle x^{frac{n}{2}}) 的逆元 (B') . (我们常常令 (n)(2^k) )

    (displaystyle A * B' equiv 1 pmod {x^frac{n}{2}} ag{1})

    我们之前那个 (x^{frac{n}{2}}) 意义下也成立 ... 就有

    [displaystyle A * B equiv 1 pmod {x^{frac{n}{2}}} ag{2} ]

    ((2)-(1)) 就有

    [B - B'equiv 0 pmod{x^{frac n 2}} ]

    然后把它左右平方一下 (此处 (x^frac{n}{2}) 也可平方成 (x^n) )

    [B^2 -2BB' +B'^2 equiv 0 pmod {x^{n}} ]

    乘上一个 (A) 就得到

    [B equiv 2B' - A * B'^2 pmod {x^n} ]

    然后递归求解就行了 .

    那么复杂度就是 (T(n) = T(frac{n}{2}) + O(n log n) =O(n log n)) ...

    代码在这道题程序中有 ...

    代码

    /**************************************************************
        Problem: 3456
        User: DOFY
        Language: C++
        Result: Accepted
        Time:11956 ms
        Memory:107796 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
     
    typedef long long ll;
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
     
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
        return x * fh;
    }
     
    void File() {
    #ifdef zjp_shadow
        freopen ("3456.in", "r", stdin);
        freopen ("3456.out", "w", stdout);
    #endif
    }
     
    const int N = (1 << 21) + 5, Mod = 1004535809;
     
    ll fpm(ll x, int power) {
        ll res = 1;
        for (; power; power >>= 1, (x *= x) %= Mod)
            if (power & 1) (res *= x) %= Mod;
        return res;
    }
     
    ll fac[N], ifac[N];
     
    inline ll C(int n, int m) {
        if (n < 0 || m < 0 || n < m) return 0;
        return fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
    }
     
    void Init(int maxn) {
        fac[0] = ifac[0] = 1;
        For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod;
        ifac[maxn] = fpm(fac[maxn], Mod - 2);
        Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
    }
     
    inline int Add(int a, int b) { return ((a += b) >= Mod) ? a - Mod : a; }
    struct Number_Theory_Transform {
        int pow3[N], invpow3[N];
        inline void Init(int maxn) {
            for (int i = 2; i <= maxn; i <<= 1)
                pow3[i] = fpm(3, (Mod - 1) / i), invpow3[i] = fpm(pow3[i], Mod - 2);
        }
     
        int n, rev[N];
        inline void Get_Rev() {
            int cnt = 0; for (int i = 1; i < n; i <<= 1) ++ cnt;
            For (i, 0, n - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1));
        }
     
        void NTT(int P[], int opt) {
            For (i, 0, n - 1) if (i < rev[i]) swap(P[i], P[rev[i]]);
            for (int i = 2; i <= n; i <<= 1) {
                int p = i >> 1, Wi = opt == 1 ? pow3[i] : invpow3[i];
                for (int j = 0; j < n; j += i)
                    for (int k = 0, x = 1; k < p; ++ k, x = 1ll * x * Wi % Mod) {
                        int u = P[j + k], v = 1ll * x * P[j + k + p] % Mod;
                        P[j + k] = Add(u, v);
                        P[j + k + p] = Add(u, Mod - v);
                    }
            }
            if (opt == -1) {
                int invn = fpm(n, Mod - 2);
                For (i, 0, n - 1) P[i] = 1ll * P[i] * invn % Mod;
            }
        }
     
        int A[N], B[N];
        void Mult(int a[], int b[], int c[], int len) {
            for (n = 1; n <= len; n <<= 1); Get_Rev();
            For (i, 0, n - 1) A[i] = a[i], B[i] = b[i];
     
            NTT(A, 1); NTT(B, 1);
            For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod;
            NTT(A, -1);
     
            For (i, 0, n - 1) c[i] = A[i];
        }
     
        void Get_Inv(int a[], int b[], int len) {
            if (len == 1) { b[0] = fpm(a[0], Mod - 2); return ; }
            Get_Inv(a, b, len >> 1);
     
            n = len << 1; Get_Rev();
            For (i, 0, len - 1) A[i] = a[i], B[i] = b[i];
     
            NTT(A, 1); NTT(B, 1);
            For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod * B[i] % Mod;
            NTT(A, - 1);
     
            For (i, 0, len - 1)
                b[i] = Add(Add(b[i], b[i]), Mod - A[i]);
        }
    } T;
     
    int a[N], b[N], c[N], tmp[N], n;
     
    int Edge(int x, int nowmod) { return 1ll * x * (x - 1) / 2 % nowmod; }
     
    int main () {
        File();
        n = read();
        T.Init(1 << 20); Init(n);
     
        For (i, 0, n)
            b[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i] % Mod;
     
        For (i, 0, n)
            c[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i - 1] % Mod;
     
        int len = 1; for (; len <= n; len <<= 1);
        T.Get_Inv(b, tmp, len); T.Mult(tmp, c, a, len);
     
        printf ("%lld
    ", 1ll * a[n] * fac[n - 1] % Mod);
     
        return 0;
    }
    
  • 相关阅读:
    面向对象基础
    VmWare下安装CentOS6图文安装教程
    设计模式培训之一:为什么要用单例模式?
    CentOS5.4下安装和配置Apache、PHP、MySql、PHPMyAdmin
    WEB架构师成长系列索引
    WEB架构师成长之路之三架构师都要懂哪些知识
    设计模式培训之三:抽象工厂
    IOS6屏幕自动旋转设置测试
    设计模式培训之二:简单工厂、工厂方法
    QT和Oracle连接的oci驱动的编译
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9163413.html
Copyright © 2011-2022 走看看