[SDOI2015]序列统计

很有趣的一道题目,很巧妙。

显然是一个dp,考虑最朴素的dp,f[i][j]表示选i个乘起来,%m为j的方案数为多少。转移也很简单。

然而乘法的转移并不能进行什么优化,于是考虑设法将其转为加法。

我们可以通过求出m的原根,因为原根G,G^i %m(1<=i<m)是一一对应1<=x<m的所以我们将原本S集合中的元素由"x"替换为"i",这样就完成了由乘法向加法的转换,但是需要注意的是,此时的模数并不再是m而是m-1,。下文中的m均为输入的m-1后的值。

那么问题就转换为了从一堆数中选n个加起来,最终%m为j的方案数为多少,如果我们用g数组来表示某一个元素是否存在,我们发现f[i+1][j]是由f[i][k]*g[(j-k+m)%m](0<=k<m)转移而来,我们发现他很像卷积的形式,但是%m怎么处理呢?

这里get了一个新技能,好像叫循环卷积。

就是计算f和g的卷积后,对于m<=i<l这一段,将他的值加到i%m上去。

f数组自然是可以重复使用的。

那么我们重复卷积n次,得到的即为最终答案。

然而n太大了。

卷积是具有交换律和结合律的,所以可以用快速幂来加速这一过程。

整体复杂度mlogmlogn

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int inf=4e4+10;
 4 const int mod=1004535809;
 5 int n,m,x,s;
 6 int a[inf];
 7 int fast(int x,int y,int z){
 8     int ans=1;
 9     while(y){
10         if(y&1)ans=1ll*ans*x%z;
11         x=1ll*x*x%z;
12         y>>=1;
13     }
14     return ans;
15 }
16 namespace RT{
17     int tmp[inf],cnt;
18     bool check(int x,int y){
19         for(int i=1;i<=cnt;i++){
20             if(::fast(x,(y-1)/tmp[i],y)==1)return 0;
21         }
22         return 1;
23     }
24     int get_rt(int x){
25         int u=x;
26         x--;
27         for(int i=2;i*i<=x;i++){
28             if(x%i)continue;
29             tmp[++cnt]=i;
30             while(x%i==0)x/=i;
31         }
32         if(x>1)tmp[++cnt]=x;
33         int now=2;
34         while(1){
35             if(check(now,u))return now;
36             now++;
37         }
38     }
39 }
40 int hs[inf];
41 int g[inf],f[inf];
42 int r[inf];
43 void ntt(int *a,int l,int type){
44     for(int i=0;i<l;i++)
45         if(i<r[i])swap(a[i],a[r[i]]);
46     for(int i=2;i<=l;i<<=1){
47         int wn=fast(3,(type*(mod-1)/i+(mod-1))%(mod-1),mod);
48         for(int j=0;j<l;j+=i){
49             int w=1;
50             for(int k=j;k<j+i/2;k++,w=1ll*w*wn%mod){
51                 int u=a[k],v=1ll*w*a[k+i/2]%mod;
52                 a[k]=(u+v)%mod;
53                 a[k+i/2]=(u-v+mod)%mod;
54             }
55         }
56     }
57 }
58 int main()
59 {
60     freopen("sdoi2015_sequence.in","r",stdin);
61     freopen("sdoi2015_sequence.out","w",stdout);
62     scanf("%d%d%d%d",&n,&m,&x,&s);
63     for(int i=1;i<=s;i++)scanf("%d",&a[i]);
64     int G=RT::get_rt(m);
65     for(int i=1;i<m;i++)hs[fast(G,i,m)]=i;
66     m--;
67     for(int i=1;i<=s;i++)
68         if(a[i])g[hs[a[i]]%m]=1;
69     int l=1,h=0;
70     while(l<m*2-1)l<<=1,h++;
71     for(int i=0;i<l;i++)
72         r[i]=(r[i>>1]>>1)+((i&1)<<(h-1));
73     int inv=fast(l,mod-2,mod);
74     f[0]=1;
75     while(n){
76         ntt(g,l,1);
77         if(n&1){
78             ntt(f,l,1);
79             for(int i=0;i<l;i++)f[i]=1ll*f[i]*g[i]%mod;
80             ntt(f,l,-1);
81             for(int i=0;i<l;i++)f[i]=1ll*f[i]*inv%mod;
82             for(int i=m;i<l;i++)f[i%m]=(f[i%m]+f[i])%mod,f[i]=0;
83         }
84         for(int i=0;i<l;i++)g[i]=1ll*g[i]*g[i]%mod;
85         ntt(g,l,-1);
86         for(int i=0;i<l;i++)g[i]=1ll*g[i]*inv%mod;
87         for(int i=m;i<l;i++)g[i%m]=(g[i%m]+g[i])%mod,g[i]=0;
88         n>>=1;
89     }
90     printf("%d\n",f[hs[x]%m]);
91     return 0;
92 }

View Code

转载于:https://www.cnblogs.com/hyghb/p/8519413.html

[SDOI2015]序列统计相关推荐

  1. P3321 [SDOI2015]序列统计(离散对数下NTT,乘法换加法)

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Weblink https://www.luogu.com.cn/problem/P3321 Prob ...

  2. Luogu P3321 [SDOI2015]序列统计

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

  3. 洛谷 - P3321 [SDOI2015]序列统计(原根+NTT)

    题目链接:点击查看 题目大意:给出一个集合 SSS,集合中的数是 [0,m)[0,m)[0,m) 且互不相同的,问从集合中选 nnn 次数字,且乘积对 mmm 取模后等于 xxx 的方案数有多少 题目 ...

  4. P3321 [SDOI2015]序列统计(未解决)

    P3321 [SDOI2015]序列统计 题意: 题解: 参考题解: 题解 P3321 [[SDOI2015]序列统计] [LG3321][SDOI2015]序列统计 神仙题..学透再补 代码:

  5. 算法学习FFT系列(2):快速数论变换NTT bzoj3992: [SDOI2015]序列统计例题详解

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

  6. BZOJ3992[SDOI2015]序列统计

    题目链接 洛谷 BZOJ 解析 头一回知道原根还可以这么考-- 不难想到递推的做法\(dp[i][j]\)表示长度为\(i\),乘积为\(j\)的答案,那么\(dp[i][j \cdot a[i] \ ...

  7. P3321 [SDOI2015]序列统计

    思路 首先有个挺显然的DP \[ dp[i][(j*k)\%m]+=dp[i-1][j]\times dp[i-1][k] \] 想办法优化这个DP 这个dp也可以写成这样 \[ dp[i][j]=\ ...

  8. BZOJ3992:[SDOI2015]序列统计——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=3992 https://www.luogu.org/problemnew/show/P3321 小C ...

  9. BZOJ 3992 [SDOI2015]序列统计

    数列长度到了109,转移矩阵边长n到了8000,除了FFT还能怎么写??!! 当然,这题由于取模,必须用NTT. 同时由于取得是乘积,所以用m的原根来搞,每次NTT完了,把后面的部分加到前面去. 注意 ...

最新文章

  1. python数据分析实训大纲,数据分析大赛考纲:(二)Python数据分析应会部分
  2. 机器学习(18)-- SVM支持向量机(根据身高体重分类性别)
  3. oracle aced什么资质,ACOUG 中国行—暨Oracle WDP云数据之旅,火热报名中……
  4. containers文件夹可以删除吗_电脑进行C盘清理,appdata文件夹可以删除吗?
  5. 宝塔面板 windows 2012 mysql 允许远程连接
  6. LeetCode 1945. 字符串转化后的各位数字之和
  7. python机器学习案例系列教程——聚类算法总结
  8. ios 进入后台 一段时间在进入前台 动画消失
  9. 社交网络影响力最大化
  10. Android 分贝测试仪功能,华为移动终端开发
  11. 广义线性模型之泊松回归
  12. 高通平台文档下载【学习笔记】
  13. 未来几年,自动化发展趋势展望
  14. oracle数据库用户状态失效Expired
  15. win7系统盘瘦身秘诀
  16. Oracle创建数据库连接——DATABASE LINKS
  17. 【Mysql】Mysql GTID复制进程出现异常,出现断点
  18. css解决图片缩小变模糊问题
  19. 使用ip小魔棒让外部网络访问内网中的资源
  20. ionic3 版本更新

热门文章

  1. jdbc连接oracle数据库
  2. java ajax级联_jQuery ajax级联二级菜单(转)
  3. Opencv---remap函数的实现
  4. 误删/var/lib/dpkg/info,文件解决方案(是否完全解决,不确定)
  5. java timetasker_Java网络与多线程系列之1:实现一个简单的对象池
  6. Virtual Treeview 5 0 0的安装以及入门
  7. 推荐系统知识梳理——矩阵分解
  8. Java7并发编程指南——第七章:定制并发类
  9. notepad自动对齐html代码,notepad如何存储为html格式化
  10. 风控策略和模型的区别_风控策略概述