https://www.luogu.org/problemnew/show/P3702

题目让我们在 $ [1, m] $ 从中选出 $ n $ 个数,当中要有 > $ 0 $ 个质数,和是 $ p $ 的倍数,直接求是很难求的,我们可以用所有方案减去不含素数的方案,这样求出来的东西就是答案

现在我们考虑如何计算出和是 $ p $ 的倍数的方案

我们令 $ f (x) $ 表示当前选出的数的和模 $ p $ 是 $ x $ 的方案数,把两个这样的多项式进行卷积

设两个多项式分别为 $ f1 $ 和 $ f2 $

$ Ans[i + j - 1] $ 这一项的值包含 $ f1[i] * f2[j] $ 的值,而其他项不包含

我们把下标 $ -1 $ 后

$ Ans[i + j - 2] $ 这一项的值包含 $ f1[i - 1] * f2[j - 1] $ 的值,而其他项不包含

再回到多项式的定义,我们发现,$ Ans[i + j - 2] += f1[i - 1] * f2[j - 1] $ 的意义就是选出的和模 $ p $ 是 $ i - 1 $ 的情况和选出的和模 $ p $ 是 $ j - 1 $ 合在一起,即选出来的数的和模 $ p $ 是 $ i + j - 2 $ 的方案数

所以我们可以先构造出初始的多项式 $ f $,求出 $ f^n(p) $ 的值,即为方案数

然后答案就是所有方案减去不含素数的方案

卷积可以写一个三模 NTT,当然这题数据范围小,直接暴力卷积也可以(跑的比 NTT 还快),但是 FFT 会爆精度(至少我爆了精度),建议暴力卷积

我的代码中有三种卷积方法的实现,可以作为参考

  1. NTT
#include <bits/stdc++.h>
#define double long double
#define CIOS ios::sync_with_stdio(false);
#define For(i, a, b) for(register int i = a; i <= b; i++)
#define Rof(i, a, b) for(register int i = a; i >= b; i--)
#define DEBUG(x) cerr << "DEBUG" << x << " >>> ";
using namespace std; typedef unsigned long long ull;
typedef long long ll; template <typename T>
inline void read(T &f) {
f = 0; T fu = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') fu = -1; c = getchar();}
while(c >= '0' && c <= '9') {f = (f << 3) + (f << 1) + (c & 15); c = getchar();}
f *= fu;
} template <typename T>
void print(T x) {
if(x < 0) putchar('-'), x = -x;
if(x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
} template <typename T>
void print(T x, char t) {
print(x); putchar(t);
} const int N = 405, mod = 20170408;
inline int mul(int x, int y) { return (int)(1ll * x * y % (ll)mod); } int r[N], n, m, p, len = 1, tot = 0;
ll A[N], B[N], C[N], ans[N], a[N], b[N], c[N], d[N]; const int P[3] = {469762049, 998244353, 167772161}, G = 3, Gi[3] = {P[0] / G + 1, P[1] / G + 1, P[2] / G + 1}; ll fpow(ll x, ll y, ll p) {
ll ans = 1;
while(y) {
if(y & 1) ans = ans * x % p;
y >>= 1; x = x * x % p;
}
return ans;
} void NTT(ll *a, int tp, int t) {
for(register int i = 1; i < len; i++) if(i < r[i]) swap(a[i], a[r[i]]);
for(register int mid = 1; mid < len; mid <<= 1) {
ll wn = fpow(tp == 1 ? G : Gi[t], (P[t] - 1) / (mid << 1), P[t]);
for(register int i = 0; i < len; i += (mid << 1)) {
ll w = 1;
for(register int j = 0; j < mid; j++, w = w * wn % P[t]) {
ll x = a[i + j], y = a[i + j + mid] * w % P[t];
a[i + j] = (x + y) % P[t]; a[i + j + mid] = (x - y + P[t]) % P[t];
}
}
}
if(tp == -1) {
ll inv = fpow(len, P[t] - 2, P[t]);
for(register int i = 0; i < len; i++) a[i] = a[i] * inv % P[t];
}
} ll inv(ll x, ll y) { return fpow(x, y - 2, y); } ll times(ll x, ll y, ll p) {
ll ans = 0;
while(y) {
if(y & 1) ans = (ans + x) % p;
y >>= 1; x = (x + x) % p;
}
return ans;
} void mul(ll *A, ll *B, ll *ans) {
for(register int i = 1; i <= p; i++) A[i - 1] = A[i], B[i - 1] = B[i]; A[p] = B[p] = 0;
len = 1; tot = 0;
while(len <= (p << 1)) len <<= 1, tot++;
for(register int i = 1; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (tot - 1));
memcpy(a, A, sizeof(a)); memcpy(b, A, sizeof(b)); memcpy(c, A, sizeof(c));
memcpy(d, B, sizeof(d)); NTT(a, 1, 0); NTT(d, 1, 0); for(register int i = 0; i < len; i++) a[i] = a[i] * d[i] % P[0]; NTT(a, -1, 0);
memcpy(d, B, sizeof(d)); NTT(b, 1, 1); NTT(d, 1, 1); for(register int i = 0; i < len; i++) b[i] = b[i] * d[i] % P[1]; NTT(b, -1, 1);
memcpy(d, B, sizeof(d)); NTT(c, 1, 2); NTT(d, 1, 2); for(register int i = 0; i < len; i++) c[i] = c[i] * d[i] % P[2]; NTT(c, -1, 2);
for(register int i = 0; i <= (p << 1); i++) {
ll p = 1ll * P[0] * P[1];
ll s = times(a[i], times(P[1], inv(P[1], P[0]), p), p) + times(b[i], times(P[0], inv(P[0], P[1]), p), p); s %= p;
ll k = (c[i] % P[2] - s % P[2] + P[2]) % P[2] * inv(p % P[2], P[2]) % P[2];
ll Ans = (s + (k * P[0] % mod * P[1] % mod)) % mod;
ans[i + 1] = Ans;
}
for(register int i = 1; i <= p; i++) ans[i] = (ans[i] + ans[i + p]) % mod, ans[i + p] = 0;
for(register int i = p; i >= 1; i--) A[i] = A[i - 1], B[i] = B[i - 1]; A[0] = B[0] = 0;
} int fpow(int x, int y) {
int ans = 1;
while(y) {
if(y & 1) ans = mul(ans, x);
y >>= 1; x = mul(x, x);
}
return ans;
} int pri[5000000], plen;
bool isp[20000005]; void init_pri(int n, ll *a) {
isp[1] = 1;
for(register int i = 2; i <= n; i++) {
if(!isp[i]) { pri[++plen] = i; }
for(register int j = 1; j <= plen && i * pri[j] <= n; j++) {
isp[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}
for(register int i = 1; i <= p; i++) a[i] = 0;
for(register int i = 1; i <= n; i++) if(isp[i]) ++a[i % p + 1];
} int main() {
read(n); read(m); read(p); init_pri(m, A); int t = n;
ans[1] = 1;
while(n) {
if(n & 1) {
memcpy(B, ans, sizeof(B));
mul(A, B, ans);
}
n >>= 1;
memcpy(B, A, sizeof(B));
memcpy(C, A, sizeof(C));
mul(B, C, A);
}
int fir = ans[1];
memset(ans, 0, sizeof(ans));
memset(A, 0, sizeof(A));
for(register int i = 1; i <= m; i++) ++A[i % p + 1];
ans[1] = 1; n = t;
while(n) {
if(n & 1) {
memcpy(B, ans, sizeof(B));
mul(A, B, ans);
}
n >>= 1;
memcpy(B, A, sizeof(B));
memcpy(C, A, sizeof(C));
mul(B, C, A);
}
cout << (ans[1] - fir + mod) % mod << endl;
return 0;
}

2.FFT & 暴力卷积(当然 FFT 是错误的)

#include <bits/stdc++.h>
#define CIOS ios::sync_with_stdio(false);
#define For(i, a, b) for(register int i = a; i <= b; i++)
#define Rof(i, a, b) for(register int i = a; i >= b; i--)
#define DEBUG(x) cerr << "DEBUG" << x << " >>> ";
using namespace std; typedef unsigned long long ull;
typedef long long ll; template <typename T>
inline void read(T &f) {
f = 0; T fu = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') fu = -1; c = getchar();}
while(c >= '0' && c <= '9') {f = (f << 3) + (f << 1) + (c & 15); c = getchar();}
f *= fu;
} template <typename T>
void print(T x) {
if(x < 0) putchar('-'), x = -x;
if(x < 10) putchar(x + 48);
else print(x / 10), putchar(x % 10 + 48);
} template <typename T>
void print(T x, char t) {
print(x); putchar(t);
} const int N = 405, P = 20170408;
inline int mul(int x, int y) { return (int)(1ll * x * y % (ll)P); } /*const double PI = acos(-1.0); struct cp {
double x, y;
cp (double xx = 0, double yy = 0) {
x = xx; y = yy;
}
}a[N], b[N]; cp operator + (const cp a, const cp b) { return cp(a.x + b.x, a.y + b.y); }
cp operator - (const cp a, const cp b) { return cp(a.x - b.x, a.y - b.y); }
cp operator * (const cp a, const cp b) { return cp(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); }*/ int r[N], n, m, p, len = 1, tot = 0;
int A[N], B[N], C[N], ans[N]; /*void FFT(cp *a, int d) {
for(register int i = 1; i < len; i++) if(i < r[i]) swap(a[i], a[r[i]]);
for(register int mid = 1; mid < len; mid <<= 1) {
cp wn = cp(cos(PI / mid), sin(PI / mid) * d);
for(register int i = 0; i < len; i += (mid << 1)) {
cp w = cp(1.0, 0.0);
for(register int j = 0; j < mid; j++, w = w * wn) {
cp x = a[i + j], y = a[i + j + mid] * w;
a[i + j] = x + y; a[i + j + mid] = x - y;
}
}
}
}*/ /*void mul(int *A, int *B, int *ans) {
len = 1, tot = 0;
while(len <= (p << 1)) len <<= 1, tot++;
for(register int i = 1; i <= len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (tot - 1));
for(register int i = 1; i <= len; i++) a[i - 1] = cp(A[i], 0), b[i - 1] = cp(B[i], 0);
FFT(a, 1); FFT(b, 1);
for(register int i = 0; i < len; i++) a[i] = a[i] * b[i];
FFT(a, -1);
for(register int i = 1; i <= len; i++) ans[i] = (int)((ll)(a[i - 1].x / (double)len + 0.5) % (ll)P);
for(register int i = 1; i <= p; i++) ans[i] = (ans[i] + ans[i + p]) % P, ans[i + p] = 0;
}*/ /*void mul(int *A, int *B, int *ans) {
for(register int i = 1; i <= p << 1; i++) A[i - 1] = A[i], B[i - 1] = B[i], ans[i] = 0; ans[0] = 0;
for(register int i = 0; i < p; i++) {
for(register int j = 0; j < p; j++) {
ans[i + j] += mul(A[i], B[j]);
if(ans[i + j] >= P) ans[i + j] -= P;
}
}
for(register int i = p << 1; i >= 1; i--) ans[i] = ans[i - 1], A[i] = A[i - 1], B[i] = B[i - 1]; A[0] = B[0] = 0;
for(register int i = 1; i <= p; i++) ans[i] = (ans[i] + ans[i + p]) % P, ans[i + p] = 0;
}*/ void mul(int *A, int *B, int *ans) {
memset(ans, 0, (N + 1) * 4);
for(register int i = 1; i <= p; i++) {
for(register int j = 1; j <= p; j++) {
ans[i + j - 1] += mul(A[i], B[j]);
ans[i + j - 1] %= P;
}
}
for(register int i = 1; i <= p; i++) ans[i] = (ans[i] + ans[i + p]) % P, ans[i + p] = 0;
} int fpow(int x, int y) {
int ans = 1;
while(y) {
if(y & 1) ans = mul(ans, x);
y >>= 1; x = mul(x, x);
}
return ans;
} int pri[5000000], plen;
bool isp[20000005]; void init_pri(int n, int *a) {
isp[1] = 1;
for(register int i = 2; i <= n; i++) {
if(!isp[i]) { pri[++plen] = i; }
for(register int j = 1; j <= plen && i * pri[j] <= n; j++) {
isp[i * pri[j]] = 1;
if(i % pri[j] == 0) break;
}
}
for(register int i = 1; i <= p; i++) a[i] = 0;
for(register int i = 1; i <= n; i++) if(isp[i]) ++a[i % p + 1];
} int main() {
read(n); read(m); read(p); init_pri(m, A); int t = n;
ans[1] = 1;
while(n) {
if(n & 1) {
memcpy(B, ans, sizeof(B));
mul(A, B, ans);
}
n >>= 1;
memcpy(B, A, sizeof(B));
memcpy(C, A, sizeof(C));
mul(B, C, A);
}
int fir = ans[1];
memset(ans, 0, sizeof(ans));
memset(A, 0, sizeof(A));
for(register int i = 1; i <= m; i++) ++A[i % p + 1];
ans[1] = 1; n = t;
while(n) {
if(n & 1) {
memcpy(B, ans, sizeof(B));
mul(A, B, ans);
}
n >>= 1;
memcpy(B, A, sizeof(B));
memcpy(C, A, sizeof(C));
mul(B, C, A);
}
cout << (ans[1] - fir + P) % P << endl;
return 0;
}

luoguP3702 [SDOI2017]序列计数的更多相关文章

  1. [BZOJ 4818/LuoguP3702][SDOI2017] 序列计数 (矩阵加速DP)

    题面: 传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=4818 Solution 看到这道题,我们不妨先考虑一下20分怎么搞 想到暴力,本蒟 ...

  2. [Sdoi2017]序列计数 [矩阵快速幂]

    [Sdoi2017]序列计数 题意:长为\(n \le 10^9\)由不超过\(m \le 2 \cdot 10^7\)的正整数构成的和为\(t\le 100\)的倍数且至少有一个质数的序列个数 总- ...

  3. BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法

    BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ...

  4. 【BZOJ 4818】 4818: [Sdoi2017]序列计数 (矩阵乘法、容斥计数)

    4818: [Sdoi2017]序列计数 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 560  Solved: 359 Description Al ...

  5. P3702 [SDOI2017]序列计数

    P3702 [SDOI2017]序列计数 链接 分析: 首先可以容斥掉,用总的减去一个质数也没有的. 然后可以dp了,f[i][j]表示到第i个数,和在模p下是j的方案数,矩阵快速幂即可. 另一种方法 ...

  6. 【BZOJ4818】[Sdoi2017]序列计数 DP+矩阵乘法

    [BZOJ4818][Sdoi2017]序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ,这n个数 ...

  7. BZOJ4818 LOJ2002 SDOI2017 序列计数 【矩阵快速幂优化DP】*

    BZOJ4818 LOJ2002 SDOI2017 序列计数 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数. Alice还希 ...

  8. [BZOJ4818][SDOI2017]序列计数(动规+快速幂)

    4818: [Sdoi2017]序列计数 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 972  Solved: 581[Submit][Status ...

  9. [bzoj4818][Sdoi2017]序列计数_矩阵乘法_欧拉筛

    [Sdoi2017]序列计数 题目大意:https://www.lydsy.com/JudgeOnline/problem.php?id=4818. 题解: 首先列出来一个递推式子 $f[i][0]$ ...

随机推荐

  1. jmeter 读取mysql数据库

    业务背景 当我们用jmeter进行压测,或者造数据的时候,我们可能希望每次请求的参数都是随机的.如果从一个文件里读取,很难达到要求.jmeter提供了一套读取数据库的组件,能满足部分要求.但性能不好, ...

  2. android-tip-SocketException之ETIMEDOUT

    异常出现时间 如果我们有一个长连接,此时网络被关闭,或者暂时失去信号, 此时就会出现此异常. 如果出现此异常,则不得不重连.

  3. [erlang 001] erlang中的错误及异常处理

    一. erlang中的错误 1. 分类 1) 编译错误:主要是编译器检测出的代码语法错误: 2) 逻辑错误:是指程序没有完成预期的工作,属于开发人员的问题: 3) 运行时错误:是指erlang运行时抛 ...

  4. Linux的kickstart安装详解

    Linux的kickstart安装详解 一.什么是kickstart? kickstart安装是redhat开创的按照你设计好的方式全自动安装系统的方式.安装方式可以分为光盘.硬盘.和网络.此文将以网 ...

  5. spring4-4-jdbc-02

    1.简化 JDBC 模板查询 每次使用都创建一个 JdbcTemplate 的新实例, 这种做法效率很低下. JdbcTemplate 类被设计成为线程安全的, 所以可以再 IOC 容器中声明它的单个 ...

  6. Python操作Excel删除一个Sheet

    在使用Python进行数据分析处理,操作Excel,有时需要删除某个Excel里的某个sheet,这里记录一个我测试成功的一个办法 软件环境: 1.OS:Win 10 64位 2.Python 3.7 ...

  7. js 数字游戏

    在某网站看到一道js题,觉得有点意思 Some numbers have funny properties. For example: 89 --> 8¹ + 9² = 89 * 1 695 - ...

  8. Redis初学笔记

    1.官网概述 Redis is an open source (BSD licensed), in-memory data structure store, used as database, cac ...

  9. HUST数媒1501班第2周作业成绩公布

    说明 本次公布的成绩对应的作业为: 第2周个人作业:WordCount编码和测试 如果同学对作业成绩存在异议,在成绩公布的72小时内(截止日期4月26日0点)可以进行申诉,方式如下: 毕博平台的第二周 ...

  10. 我的ubuntu14.04配置

    完全卸载java(移除所有 Java相关包 (Sun, Oracle, OpenJDK, IcedTea plugins, GIJ)) 转载修改, 原文见:http://blog.csdn.net/s ...