LibreOJ #2002. 「SDOI2017」序列计数

二次联通门 : LibreOJ #2002. 「SDOI2017」序列计数

/*
    LibreOJ #2002. 「SDOI2017」序列计数
    线性筛 + 矩阵优化dp

    先构造出全部情况的矩阵
    用矩阵快速幂计算答案
    再构造出全不是质数的矩阵
    计算出答案
    前一个答案减后一个答案即可
*/
#include <cstdio>
#include <iostream>
#include <cstring>

const int BUF = 12312312;
char Buf[BUF], *buf = Buf;

#define Mod 20170408

inline void read (int &now)
{
    for (now = 0; !isdigit (*buf); ++ buf);
    for (; isdigit (*buf); now = now * 10 + *buf - ‘0‘, ++ buf);
}

#define Max 20000007
#define L 100
struct Matrix
{
    int c[L][L];
    Matrix () { memset (c, 0, sizeof c); }

    Matrix operator * (const Matrix &now) const
    {
        Matrix res;
        for (register int i = 0, j, k; i < L; ++ i)
            for (k = 0; k < L; ++ k)
                if (this->c[i][k])
                    for (j = 0; j < L; ++ j)
                        res.c[i][j] = (res.c[i][j] + 1LL * this->c[i][k] * now.c[k][j]) % Mod;
        return res;
    }
};

int operator ^ (Matrix A, int p)
{
    Matrix res; res.c[0][0] = 1;
    for (; p; A = A * A, p >>= 1)
        if (p & 1)
            res = res * A;
    return res.c[0][0];
}

Matrix A;
int prime[Max];
bool is[Max];
int C;

void Euler (const int N)
{
    is[1] = true;
    for (register int i = 2, j; i <= N; ++ i)
    {
        if (!is[i]) prime[++ C] = i;
        for (j = 1; i * prime[j] <= N && j <= C; ++ j)
        {
            is[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

int data[Max];

int Main ()
{
    fread (buf, 1, BUF, stdin);
    int N, M, K; read (N), read (M), read (K);
    Euler (Max - 7); register int i, j;

    for (i = 1; i <= M; ++ i) ++ data[i % K];
    for (i = 0; i < K; ++ i)
        for (j = 0; j < K; ++ j)
            A.c[i][(i + j) % K] = data[j] % Mod;
    int Answer = A ^ N; memset (A.c, 0, sizeof A.c);
    memset (data, 0, sizeof data);
    for (i = 1; i <= M; ++ i)
        if (is[i]) ++ data[i % K];
    for (i = 0; i < K; ++ i)
        for (j = 0; j < K; ++ j)
            A.c[i][(i + j) % K] = data[j] % Mod;
    Answer -= A ^ N;

    printf ("%d", (Answer + Mod) % Mod);
    return 0;
}
int ZlycerQan = Main ();
int main (int argc, char *argv[]) {;}
时间: 2024-10-10 23:31:30

LibreOJ #2002. 「SDOI2017」序列计数的相关文章

loj#2002. 「SDOI2017」序列计数(dp 矩阵乘法)

题意 题目链接 Sol 质数的限制并没有什么卵用,直接容斥一下:答案 = 忽略质数总的方案 - 没有质数的方案 那么直接dp,设\(f[i][j]\)表示到第i个位置,当前和为j的方案数 \(f[i + 1][(j + k) \% p] += f[i][j]\) 矩乘优化一下. #include<bits/stdc++.h> #define LL long long using namespace std; const int MAXN = 2e7 + 10, mod = 20170408,

AC日记——「SDOI2017」序列计数 LibreOJ 2002

「SDOI2017」序列计数 思路: 矩阵快速幂: 代码: #include <bits/stdc++.h> using namespace std; #define mod 20170408 #define ll long long struct MatrixType { int n,m; ll ai[105][105]; void mem(int n_,int m_) { n=n_,m=m_; for(int i=0;i<=n;i++) for(int v=0;v<=m;v++

loj2004. 「SDOI2017」硬币游戏

2004. 「SDOI2017」硬币游戏 周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利. 大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了. 同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况. 用 $ \texttt{H} $ 表示正面朝上, 用 $ \texttt{T} $ 表示反面朝上,扔很多次硬币后,会得到一个硬币序列.比如 $ \texttt{HTT} $ 表示第一次正面朝上,后两次反面朝上. 但扔到什么时

LibreOJ #2012. 「SCOI2016」背单词

二次联通门 : LibreOJ #2012. 「SCOI2016」背单词 /* LibreOJ #2012. 「SCOI2016」背单词 Trie + 贪心 大家都吐槽题目反人类 可我觉得还好,毕竟见的多了 不会做啊.. 正解好巧妙 考虑一下,发现一操作完全不必要,可以省去 因为所有的字符串的后缀关系会形成一个树 那么把字符串倒序插入Trie中 建树,每次向子树小的一个点转移即可 */ #include <cstdio> #include <algorithm> #include

LibreOJ #2006. 「SCOI2015」小凸玩矩阵

二次联通门 : LibreOJ #2006. 「SCOI2015」小凸玩矩阵 /* LibreOJ #2006. 「SCOI2015」小凸玩矩阵 本来以为是道数据结构题 后来想了想发现不可做 就考虑二分dp判断 推方程推不出来 就考虑用网络流判断了 二分出一个数 将小于这个数的位置的点编号 每行的可行点与下一行可行的点连边 后一边最大流判断可选出的数的个数是否符合要求即可 */ #include <cstdio> #include <iostream> #include <q

LibreOJ #2016. 「SCOI2016」美味

二次联通门 : LibreOJ #2016. 「SCOI2016」美味 /* LibreOJ #2016. 「SCOI2016」美味 dalao们都在说这题如果没有加法balabala就可以用可持久化trie解决了 然而我连那个也不会啊QAQ 此题用主席树 从高位到低位贪心 能填1就填1,也就是查询一段区间有没有某个范围的数 (然而由乃dalao说可持久化线段树和可持久化trie是一个东西) */ #include <cstdio> #include <iostream> #inc

LibreOJ #2033. 「SDOI2016」生成魔咒

二次联通门 : LibreOJ #2033. 「SDOI2016」生成魔咒 /* LibreOJ #2033. 「SDOI2016」生成魔咒 调了整整一天啊... 绝望啊 最后发现是1打成i了啊!!! */ #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <map> const int BUF = 10000020; char

LibreOJ #2061. 「HAOI2016」放棋子

二次连通门 : LibreOJ #2061. 「HAOI2016」放棋子 /* LibreOJ #2061. 「HAOI2016」放棋子 MDZZ ... 错排 + 高精 */ #include <iostream> #include <cstdio> #include <vector> #include <iomanip> #include <cassert> #include <algorithm> #define int64 l

LibreOJ #2219. 「HEOI2014」大工程

二次联通门 : LibreOJ #2219. 「HEOI2014」大工程 /* LibreOJ #2219. 「HEOI2014」大工程 虚树 + dp 对于每次的关键点建好虚树后 考虑树形dp dp[i] 表示在以i为根的子树路径总长 size[i] 表示在以i为根的子树中关键点的个数 maxn为以i为根的子树中到关键点到根最长的路径长 ans1自己推一推就好 对于ans2 如果i是关键点,则直接用maxn[i]更新答案 否则用maxn[i]+maxn[child[i]]+dis(i,son[