zoukankan      html  css  js  c++  java
  • Luogu 3702 [SDOI2017]序列计数

    BZOJ 4818

    感觉不难。

    首先转化一下题目,“至少有一个质数”$=$“全部方案”$ - $“一个质数也没有”。

    注意到$m leq 2e7$,$[1, m]$内的质数可以直接筛出来。

    设$f_{i, j}$表示当前长度序列为$i$,当前和模$p$的值是$j$的方案数,直接无脑枚举$m$转移复杂度是$O(nmp)$的,但是发现每一次转移形式都是相同的。

    $$f_{i, x} = sum f_{i - 1, y}(y + z equiv x(mod p))$$

    其实在模$p$的意义下大于等于$p$的数可以直接归类到这个数模$p$这一档里面,也就是说,我们可以记一个$cnt_x$表示模$p$意义下相同的数有$x$个。

    $$f_{i, (x + y) mod p} = sum f_{i - 1, x} imes cnt_y$$

    发现这个式子的形式很像矩阵快速幂的样子,然后就把转移写成矩阵的形式快速幂一下就好了。

    转移矩阵的第$(i, j)$个格子是$sum_{(i + k) equiv j(mod p)}cnt_k$

    时间复杂度$O(m + p^3logn)$。

    咕,感觉时间刚刚好。

    然而再次观察一下这个式子发现是一个卷积的形式,因此可以直接$NTT$,时间复杂度可以降到$O(m + plogplogn)$,但是在这题中$p$太小了$ + $模数不好,直接暴力卷积的时间表现应该比$NTT$要优秀。

    Code:

    #include <cstdio>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    
    const int N = 2e7 + 5;
    const int M = 100;
    const ll P = 20170408LL;
    
    int n, m, K, pCnt = 0, pri[N], cnt[M];
    bool np[N];
    
    template <typename T>
    inline void inc(T &x, T y) {
        x += y;
        if (x >= P) x -= P;
    }
    
    template <typename T>
    inline void sub(T &x, T y) {
        x -= y;
        if (x < 0) x += P;
    }
    
    struct Matrix {
        int tn, tm;
        ll s[M][M];
        
        inline void init() {
            tn = tm = 0;
            memset(s, 0, sizeof(s));
        }
        
        friend Matrix operator * (const Matrix x, const Matrix y) {
            Matrix res;
            res.init();
            res.tn = x.tn, res.tm = y.tm;
            for (int k = 0; k < x.tm; k++)
                for (int i = 0; i < x.tn; i++)
                    for (int j = 0; j < y.tm; j++)
                        inc(res.s[i][j], x.s[i][k] * y.s[k][j] % P);
            return res;
        }
        
        inline Matrix fpow(int y) {
            Matrix x = *this, res;
            res.init();
            res.tn = x.tn, res.tm = x.tm;
            for (int i = 0; i < x.tn; i++) res.s[i][i] = 1;
            for (; y ; y >>= 1) {
                if (y & 1) res = res * x;
                x = x * x;
            }
            return res;
        }
        
        inline void print() {
            for (int i = 0; i < tn; i++)
                for (int j = 0; j < tm; j++)
                    printf("%lld%c", s[i][j], " 
    "[j == tm - 1]);
            printf("
    ");
        }
        
    } trans, ans;
    
    inline void sieve() {
        np[1] = 1;
        for (int i = 2; i <= m; i++) {
            if (!np[i]) pri[++pCnt] = i;
            for (int j = 1; j <= pCnt && pri[j] * i <= m; j++) {
                np[i * pri[j]] = 1;
                if (i % pri[j] == 0) break;
            }
        }
    }
    
    inline ll solve1() {
        memset(cnt, 0, sizeof(cnt));
        for (int i = 1; i <= m; i++) ++cnt[i % K];
        
        trans.init();
        trans.tn = trans.tm = K;
        for (int i = 0; i < K; i++)
            for (int j = 0; j < K; j++)
                inc(trans.s[i][(i + j) % K], 1LL * cnt[j]);
    //    trans.print();
        
        trans = trans.fpow(n);
        
    //    trans.print();
        
        ans.init();
        ans.s[0][0] = 1;
        ans.tn = 1, ans.tm = K;
        ans = ans * trans;
        return ans.s[0][0];
    }
    
    inline ll solve2() {
        sieve();
        memset(cnt, 0, sizeof(cnt));
        for (int i = 1; i <= m; i++) 
            if (np[i]) ++cnt[i % K];
        
    /*    for (int i = 0; i < K; i++)
            printf("%d%c", cnt[i], " 
    "[i == K - 1]);    */
            
        trans.init();
        trans.tn = trans.tm = K;
        for (int i = 0; i < K; i++)
            for (int j = 0; j < K; j++)
                inc(trans.s[i][(i + j) % K], 1LL * cnt[j]);
    //    trans.print();
    
        trans = trans.fpow(n);
    
    //    trans.print();
        
        ans.init();
        ans.s[0][0] = 1;
        ans.tn = 1, ans.tm = K;
        ans = ans * trans;
        return ans.s[0][0];
    }
    
    int main() {
        scanf("%d%d%d", &n, &m, &K);
    //    printf("%lld
    ", solve1());
    //    printf("%lld
    ", solve2());
        printf("%lld
    ", (solve1() - solve2() + P) % P);
        return 0;
    }
    View Code
  • 相关阅读:
    《PS技巧精华全部在这里!》
    c#的dictionary为什么在扩容时会以素数扩容
    初识Lua
    常用的排序
    像gal一样讲故事~
    打枪的实现
    武器的方向,人物和武器的翻转
    虚拟摇杆的修改
    实现虚拟摇杆的移动
    itemPool和MessageCenter
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/10285823.html
Copyright © 2011-2022 走看看