题面

Bzoj

Sol

pts 1

大暴力很简单,\(f[i][j]\)表示到第\(i\)个位置,前面积的模为\(j\)的方案

然后可以获得\(10\)分的好成绩

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int Zsy(1004535809);
const int _(8010); IL ll Input(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
} int n, m, x, S, a[_], f[2][_]; IL void Up(RG int &x, RG int y){
x += y;
if(x >= Zsy) x -= Zsy;
} int main(RG int argc, RG char* argv[]){
n = Input(), m = Input(), x = Input(), S = Input();
for(RG int i = 1; i <= S; ++i) a[i] = Input() % m;
f[0][1] = 1;
for(RG int i = 0; i < n; ++i)
for(RG int j = 0; j < m; ++j){
RG int lst = i & 1;
if(!f[lst][j]) continue;
for(RG int k = 1; k <= S; ++k)
Up(f[lst ^ 1][1LL * j * a[k] % m], f[lst][j]);
f[lst][j] = 0;
}
printf("%d\n", f[n & 1][x]);
return 0;
}

pts 2

你会发现所有的转移都是一样的

然后你看到\(n\)的范围就想到了快速幂

那么把\(f\)设成一维,\(f[i]\)表示积的模为\(i\)的方案数

当前状态下,假设做到了第\(k\)个位置

那么此时的\(f\)数组自己和自己组合就可以转移到第\(2k\)个位置的\(f\)

那么就可以用快速幂一样的方式来优化时间

\(60\)分

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int Zsy(1004535809);
const int _(8010); IL ll Input(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
} int n, m, x, S, f[_], g[_], h[_]; IL void Up(RG int &x, RG int y){
x += y;
if(x >= Zsy) x -= Zsy;
} int main(RG int argc, RG char* argv[]){
n = Input(), m = Input(), x = Input(), S = Input();
for(RG int i = 1; i <= S; ++i) ++f[Input() % m];
h[1] = 1;
for(RG int i = n; i; i >>= 1){
if(i & 1){
for(RG int j = 0; j < m; ++j)
for(RG int k = 0; k < m; ++k)
Up(g[1LL * j * k % m], 1LL * f[j] * h[k] % Zsy);
for(RG int j = 0; j < m; ++j) h[j] = g[j], g[j] = 0;
}
for(RG int j = 0; j < m; ++j)
for(RG int k = 0; k < m; ++k)
Up(g[1LL * j * k % m], 1LL * f[j] * f[k] % Zsy);
for(RG int j = 0; j < m; ++j) f[j] = g[j], g[j] = 0;
}
printf("%d\n", h[x]);
return 0;
}

pts 3

此时的复杂度为\(O(m^2log\ n)\)

那个\(log\)显然去不掉

只能优化那个\(m^2\)

注意到每次都是\(i, j\)转移到\(i*j\)

如果它是\(i, j\)转移到\(i+j\)就好了

怎么转化?

你知道可以用指数运算转化

又注意到\(x>=1\),\(m为质数\)

还是不会

这个时候就可以去orz 题解辣

原根!!(大雾)

原根能干什么?

注意到原根的性质:

设\(g\)为质数\(p\)的原根

那么\(g\)的\(0\)到\(p-2\)的幂在模\(p\)的意义下一定不重不漏对应着\(1\)到\(p-1\)这些数

而奇质数\((>2)\)一定有原根

并且\(x>0\),那么把积取模后为\(0\)的丢掉就好了

那么我们把转移时的\(i, j\)中的\(i, j\)映射成原根的幂的指数\(i', j'\)

那么转移的\(i*j\)就变成了指数运算\(i'+j'\)(注意这里都是模\(m\)意义下的)

然后就可以愉快的\(NTT\)辣

还要注意\(NTT\)完后得到的数组是比原来长的

要把它累加到下标对\(m-1\)取模后的地方

求原根

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int Zsy(1004535809);
const int Phi(1004535808);
const int G(3);
const int _(20010); IL ll Input(){
RG ll x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
} int n, m, x, S, f[_], g[_];
int A[_], B[_], N = 1, l, r[_];
int mul[_], mp[_]; IL int Pow(RG ll x, RG ll y, RG int zsy){
RG ll ret = 1;
for(; y; y >>= 1, x = x * x % zsy)
if(y & 1) ret = ret * x % zsy;
return ret;
} IL int Pr_Rt(RG int P){
RG int phi = P - 1;
for(RG int i = 2; i * i <= phi; ++i)
if(phi % i == 0){
while(phi % i == 0) phi /= i;
mul[++mul[0]] = i;
}
if(phi > 1) mul[++mul[0]] = phi;
for(RG int i = 2; ; ++i){
RG int flg = 0;
for(RG int j = 1; j <= mul[0]; ++j)
if(Pow(i, (P - 1) / mul[j], P) == 1){
flg = 1;
break;
}
if(!flg) return i;
}
return 233;
} IL void Prepare(){
for(RG int i = m + m - 2; N <= i; N <<= 1) ++l;
for(RG int i = 0; i < N; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
RG int rt = Pr_Rt(m);
for(RG int i = 0; i < m - 1; ++i) mp[Pow(rt, i, m)] = i;
} IL void NTT(RG int *P, RG int opt){
for(RG int i = 0; i < N; ++i) if(i < r[i]) swap(P[i], P[r[i]]);
for(RG int i = 1; i < N; i <<= 1){
RG int W = Pow(G, Phi / (i << 1), Zsy);
if(opt == -1) W = Pow(W, Zsy - 2, Zsy);
for(RG int j = 0, p = i << 1; j < N; j += p){
RG int w = 1;
for(RG int k = 0; k < i; ++k, w = 1LL * w * W % Zsy){
RG int X = P[k + j], Y = 1LL * w * P[k + j + i] % Zsy;
P[k + j] = (X + Y) % Zsy, P[k + j + i] = (X - Y + Zsy) % Zsy;
}
}
}
if(opt == -1){
RG int inv = Pow(N, Zsy - 2, Zsy);
for(RG int i = 0; i < N; ++i) P[i] = 1LL * P[i] * inv % Zsy;
}
} IL void Mul(RG int *a, RG int *b){
for(RG int i = 0; i < N; ++i) A[i] = a[i], B[i] = b[i], a[i] = 0;
NTT(A, 1); NTT(B, 1);
for(RG int i = 0; i < N; ++i) A[i] = 1LL * A[i] * B[i] % Zsy;
NTT(A, -1);
for(RG int i = 0; i < N; ++i) (a[i % (m - 1)] += A[i]) %= Zsy;
} int main(RG int argc, RG char* argv[]){
n = Input(), m = Input(), x = Input(), S = Input();
Prepare();
for(RG int i = 1; i <= S; ++i){
RG int a = Input() % m;
if(a) ++f[mp[a]];
}
g[mp[1]] = 1;
for(; n; n >>= 1, Mul(f, f)) if(n & 1) Mul(g, f);
printf("%d\n", g[mp[x]]);
return 0;
}

Bzoj3992:[SDOI2015]序列统计的更多相关文章

  1. [BZOJ3992][SDOI2015]序列统计(DP+原根+NTT)

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1888  Solved: 898[Submit][Statu ...

  2. BZOJ3992: [SDOI2015]序列统计(NTT 原根 生成函数)

    题意 题目链接 给出大小为\(S\)的集合,从中选出\(N\)个数,满足他们的乘积\(\% M = X\)的方案数 Sol 神仙题Orz 首先不难列出最裸的dp方程,设\(f[i][j]\)表示选了\ ...

  3. BZOJ3992: [SDOI2015]序列统计

    Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S. 小C用这个生成器生成了许多这样的数列. ...

  4. 2018.12.31 bzoj3992: [SDOI2015]序列统计(生成函数+ntt+快速幂)

    传送门 生成函数简单题. 题意:给出一个集合A={a1,a2,...as}A=\{a_1,a_2,...a_s\}A={a1​,a2​,...as​},所有数都在[0,m−1][0,m-1][0,m− ...

  5. 【动态规划】bzoj3992 [Sdoi2015]序列统计 10分

    #include<cstdio> using namespace std; #define MOD 1004535809 int a[8001],f[1001][101],n,m,x,S; ...

  6. 【NTT】bzoj3992: [SDOI2015]序列统计

    板子题都差点不会了 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生 ...

  7. BZOJ3992 [SDOI2015]序列统计 【生成函数 + 多项式快速幂】

    题目 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数 列,数列中的每个数都属于集合S.小C用这个生成器生成了许多这样的数列.但是小C有一个问题 ...

  8. 【BZOJ3992】[SDOI2015]序列统计 NTT+多项式快速幂

    [BZOJ3992][SDOI2015]序列统计 Description 小C有一个集合S,里面的元素都是小于M的非负整数.他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属 ...

  9. BZOJ 3992: [SDOI2015]序列统计 [快速数论变换 生成函数 离散对数]

    3992: [SDOI2015]序列统计 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 466[Submit][Statu ...

  10. [SDOI2015]序列统计

    [SDOI2015]序列统计 标签: NTT 快速幂 Description 给你一个模m意义下的数集,需要用这个数集生成一个数列,使得这个数列在的乘积为x. 问方案数模\(1004535809\). ...

随机推荐

  1. shell脚本实现anisble客户端脚本分发和密钥授权配置

    ##############################Deploy ansible client shell######################## echo "start d ...

  2. 开始使用PHPUnit单元测试

    何为单元测试: 指对软件中的基本单元进行测试,如函数.方法等,以检查其返回值或行为是否符合预期:实际中软件是很复杂的,由许多组件构成,执行流程连贯在一起,要进行单元片段的测试,就需要为其提供执行上下文 ...

  3. Docker Centos6 下建立 Docker 桥接网络

    cd /etc/sysconfig/network-scripts/; cp ifcfg-eth0  ifcfg-br0 vi ifcfg-eth0 //增加BRIDGE=br0,删除IPADDR,N ...

  4. href与src 区别

    src 是可替换的文本支撑,将指向的内容引入文档当前标签所在的位置, 当浏览器解析到该标签时,将暂停其它资源的下载处理, 请求该标签的src ,下载指向的外部资源并应用到当前文档, 所以js 脚本一般 ...

  5. PHP opcache扩展安装

    下面是我在PHP 5.4下的安装方法: https://pecl.php.net/get/zendopcache-7.0.5.tgz tar xzf zendopcache-7.0.5.tgz cd ...

  6. Windows下如何硬盘安装Ubuntu

    一般来说,折腾双系统是每一位程序猿都有过的经历,如何在windows下安装双系统ubuntu呢?今天来给大家介绍一下如何直接在windows硬盘安装ubuntu,而不需要使用U盘或者光盘,或外置硬盘. ...

  7. 剑指offer 第一个只出现一次的字符 hash

    思路:i表示字符的ASCII码值,cntp[i]表示字符出现的次数. AC代码 class Solution { public: int FirstNotRepeatingChar(string st ...

  8. 用Python实现《芳华》小说中的汉字频率统计

     环境: Python 3的代码,亲测可用. 思路: 是先把每个字符提出来放在列表里:再过滤掉其中的标点符号:最后用字典对某个字出现的频率进行累加. 扩展: 用处很多,稍微改改,既可以用来统计小说或文 ...

  9. Object方法

    1. getClass() 返回此 Object 的运行时类. 2. hashCode() 返回该对象的哈希码值. 3. equals() 指示其他某个对象是否与此对象“相等”. 4. toStrin ...

  10. KVM虚拟化网络优化技术总结

    https://www.intel.com/content/dam/www/public/us/en/documents/technology-briefs/sr-iov-nfv-tech-brief ...