博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Bzoj3992:[SDOI2015]序列统计
阅读量:6823 次
发布时间:2019-06-26

本文共 5669 字,大约阅读时间需要 18 分钟。

题面

Sol

pts 1

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

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

# include 
# 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 
# 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 
# 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;}

转载于:https://www.cnblogs.com/cjoieryl/p/8443356.html

你可能感兴趣的文章
Azure实例级公共IP
查看>>
停电,导致DC无法启动
查看>>
MariaDB七之双主复制
查看>>
Sencha touch实践(1)在ios,android上变web app为native app
查看>>
XNA游戏:重力感应
查看>>
DNS服务部署的那点事儿
查看>>
【云计算的1024种玩法】使用 MSMTP 实现底层环境的 阿里云·邮件推送服务 兼容...
查看>>
Varnish介绍,安装与配置详解。
查看>>
CentOS bash漏洞威胁恐比“心脏流血”更大
查看>>
LINUX总结
查看>>
SCOM 2016监控IIS 网页服务
查看>>
通用权限管理系统组件 (GPM - General Permissions Manager) 中最简单的例子程序,如何时间通讯录管理...
查看>>
Ajax
查看>>
端口基础常识大全贴
查看>>
Cisco交换机的经典配置(2)
查看>>
稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室
查看>>
毕业论文一次性修改所有字母和数字的字体
查看>>
vsphere5.2.安装部署VMware ESXi 5 上 视频共享
查看>>
impala1.2.3 udf问题
查看>>
数据仓库专题23-原则!原则!原则!
查看>>