Portal --> 出错啦qwq(好吧其实是没有)

Description

  给定两个正整数\(n,k\),选择一些互不相同的正整数,满足这些数的最小公倍数恰好为\(n\),并且这些数的和为\(k\)的倍数

  求选择的方案数对\(232792561\)取模

  数据范围:多组数据,组数\(T<=10,n<=10^{18},k<=20\),且\(n\)的所有质因子不大于\(100\)

Solution

  这题。。好神仙啊qwq敲爆脑子都想不出来系列qwq

  注意到\(n<=10^{18}\)意味着\(n\)至多有\(15\)个不同的质因子(前\(15\)个质数乘一下就知道了),并且\(k\)的范围也是比较小的

  然后还有一点就是这个模数比较有趣,它\(=lcm(1,2,...,20)+1\),也就是说它可以对长度在\(1..20\)的区间DFT

​  那不管别的我们可以先考虑一个最朴素的dp

  记\(f[i][j]\)表示当前所选的数状态为\(i\),和\(\%k=j\)的方案数,其中这个“所选数的状态”具体计算方式是:假设写成分解质因数的形式后\(n=\sum p_i^{mi_i}\),其中\(p_i\)为\(n\)的每一个不同的质因子,我们的状态是一个二进制数,并且二进制的每一位对应一个\(n\)的质因子,对于二进制的第\(i\)位(假设对应\(p_i\)),如果说当前所选的数中存在一个数\(x\)满足\(p_i^{mi_i}|x\),那么这位为\(1\),否则为\(0\)

​  这样一来,我们最后的答案就应该是\(f[(1<<Cnt)-1][0]\),其中\(Cnt\)为\(n\)的不同质因子个数

  至于求解。。分解质因数和预处理因数可以暴力求解,但是后面的dp直接暴力转移的话稳稳的T啊(光枚举子集就要\(3^{Cnt}\)了还要再乘个\(k\)),所以现在的问题是我们要怎么比较快速地转移

​  观察这个dp两维的转移,我们会发现第一维的转移可以写成一个集合或卷积的形式(或起来等于某个数就把值累计进去),而第二维的转移则直接枚举因数什么的大力转移就好了

  然后这里有一个很有意思的想法:首先我们发现这两维如果可以分开处理就会比较好一点,然后注意到这个模数很有趣,允许我们进行区间长度\(\in[1,20]\)的DFT,而DFT操作之后,dp的这两维在某种意义上就是独立的了,换句话来说,如果说我们先在同行内转移\(f\)数组,再对转以后每行的不完全计算的\(f\)数组做一次DFT,这行的\(f\)的第二维就不会互相影响了,也就是说我们可以通过这种方式实现第一维和第二维转移的分离

  这样我们就可以先处理\(f[][i]\),直观一点来说就是先在同行转移\(f\)数组,具体一点就是枚举\(n\)的因数,然后考虑加进去的贡献,也就是假设当前枚举到的因数是\(a[i]\),\(a[i]\)对应的状态是\(st\),那么我们可以对于所有的\(j\in [0,k)\)转移:
\[ f[st][(j+a[i]\%k)]+=f'[st][j] \]
​  之所以在后面的\(f\)打了个\('\)是因为我们这里要累加进去的是用\(a[i]\)更新前的\(f[st]\)的版本

​  

​  这样我们就得到了整合了同行数据之后的\(f\)数组(为了防止弄混在接下来的描述中我们还是将这个不完全转移的\(f\)数组记为\(f1\)好了),然后对于每一行DFT一下,接下来就是考虑第一维的转移了,更加直观一点就是。。同列的\(f\)进行转移

​  接下来为了让描述变得更加简洁,我们将第二维省去(因为反正转移的时候都是同列转移)

​  现在我们要做的事情就是挑出若干个\(f1[i]\),满足\(i_1\ or\ i_2\ or\ ...\ i_m=st\)然后将这堆\(f1[i]\)的值累加到\(f[st]\)去

  我们记\(F(S)=\sum\limits_{i\subseteq S}f[i]\),如果说我们知道了\(F(S)\)的取值,那么只要大力容斥一下就可以得出\(f\)数组了,具体一点的话就是:
\[ f[T]=\sum\limits_{S\subseteq T}(-1)^{|T|-|S|}F(S) \]
  我们将\(F(S)\)进行一下转化,会发现:
\[ \begin{aligned} F(S)&=\sum\limits_{j\subseteq S}f[j]\\ &=\prod\limits_{i\subseteq S}(1+f1[i]) \end{aligned} \]
  具体的话就是因为对于\(f\)来说,每一个\(f[j]\)都是由若干个\(f1[i]\)组成的,我们考虑将第二个等号后面的连乘的括号拆掉,展开之后会发现囊括了\(i\)的所有组合方式(为了方便理解可以自己用比较小的规模模拟一下),然后因为我们限制了\(i\subseteq S\)所以可以保证任意的组合方式都是满足组合后\(\subseteq S\)的

​  然后由于\(f1\)是已知的,所以我们现在就要想如何快速求\(F(S)\)

  显然枚举子集不现实

​  如果我们直接从小到大枚举状态,每次将这个状态对应的\(f1\)值转移的话,可能会出现重复计算的情况(比如说\(010\)可以转移到\(011\)和\(110\),而\(011\)和\(110\)又都可以转移到\(111\),这样如果按照这种转移方式的话,\(111\)这里\(010\)的\(f1\)值就会被重复计算),所以这里考虑一种很玄学的按顺序转移方式:

​  我们考虑二进制一位一位转移,从低位枚举到高位,每枚举一位就把所有的这位为\(0\)的状态的值累加到将这位修改为\(1\)后的状态里面去,具体一点的话就是:

​  我们以\(Cnt=3\)为例子,状态转移大概是这样:

  其中橙黄色的箭头表示转移的方向,然后位数是从最右边开始数的

​  这样的话显然所有的状态都能转移到它能转移到的位置,现在我们来说明一下为什么这样不会重复计算:首先可以确认的一点是,这种重复计算的情况只会出现在转移到一个被修改了两个及以上位的状态的时候,而我们这样的枚举方式每次只转移到修改了一位的地方,并且是按照数位从低到高,状态从小到大的顺序转移,比如说刚才提到的\(010\)在\(111\)中被重复计算的情况,放在这个转移方式中就应该是:在转移第一位的时候,\(010\)会先转移到\(011\),然后在转移第三位的时候再由累加了\(010\)的\(011\)转移到\(111\),每次都是从只修改一位的地方转移过来,不会出现重复计算的情况(但其实上面的证明也不算。。特别严谨。。还是感性理解既视感qwq)

​  然后我们就可以在\(O(2^{Cnt})\)的时间内求得\(F(S)\),接下来直接大力容斥一下就可以得出\(f[(1<<Cnt)-1][0]\)的值最后IDFT一下就可以得出答案啦

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int MOD=232792561,G=71,N=110;
const int p[26]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
ll mi[N],rec_mx[N],Tmp[N],have[N];
ll a[500010];
int f[(1<<15)+10][20];
ll n,K,T,Cnt,all;
void add(int &x,int y){x=(1LL*x+y+MOD)%MOD;}
bool in(int st,int x){return st>>(x-1)&1;}
int St(int x){return 1<<x-1;}
int ksm(int x,int y){int ret=1,base=x;for (;y;y>>=1,base=1LL*base*base%MOD)if (y&1) ret=1LL*ret*base%MOD;return ret;
}
namespace DFT{/*{{{*/int tmp[110],W[1010][2];int inv;void init(){int rt=ksm(G,(MOD-1)/K);W[0][0]=1;for (int i=1;i<=1000;++i)W[i][0]=1LL*W[i-1][0]*rt%MOD;for (int i=0;i<=1000;++i)W[i][1]=ksm(W[i][0],MOD-2);}void dft(int *a,int n,int op){for (int i=0;i<n;++i) tmp[i]=0;for (int i=0;i<n;++i) for (int j=0;j<n;++j)tmp[i]=(1LL*tmp[i]+1LL*a[j]*W[i*j][op==-1]%MOD)%MOD;if (op==-1){inv=ksm(n,MOD-2);for (int i=0;i<n;++i) tmp[i]=1LL*tmp[i]*inv%MOD;}for (int i=0;i<n;++i) a[i]=tmp[i];}
}/*}}}*/
void Div(ll x){for (int i=1;i<=25;++i)if (x%p[i]==0){++Cnt; rec_mx[Cnt]=1; mi[Cnt]=0; have[Cnt]=p[i];while (x%p[i]==0) x/=p[i],++mi[Cnt],rec_mx[Cnt]*=p[i];}
}
void dfs(int now,ll prod){if (now>Cnt){a[++a[0]]=prod;return;}ll tmp=prod;for (int i=0;i<=mi[now];++i){dfs(now+1,tmp);tmp*=have[now];}
}
void prework(){a[0]=0; Cnt=0;Div(n);dfs(1,1);
}
void update(int st,int r){for (int i=0;i<K;++i) Tmp[i]=f[st][i];for (int i=0;i<K;++i) add(f[st][(i+r)%K],Tmp[i]);
}
void solve(){int st,op;memset(f,0,sizeof(f));all=(1<<Cnt)-1;for (int i=0;i<=all;++i) f[i][0]=1;//initfor (int i=1;i<=a[0];++i){st=0;for (int j=1;j<=Cnt;++j)if (a[i]%rec_mx[j]==0) st|=St(j);update(st,a[i]%K);}for (int i=0;i<=all;++i)DFT::dft(f[i],K,1);for (int i=1;i<=Cnt;++i)for (int j=0;j<=all;++j)if (in(j,i))for (int k=0;k<K;++k)f[j][k]=1LL*f[j][k]*f[j^St(i)][k]%MOD;for (int i=0;i<all;++i){op=1;for (int j=1;j<=Cnt;++j)if (!in(i,j)) op*=-1;for (int j=0;j<K;++j)add(f[all][j],op*f[i][j]);}DFT::dft(f[all],K,-1);printf("%d\n",f[all][0]);
}int main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);
#endifscanf("%d",&T);for (int o=1;o<=T;++o){scanf("%lld%lld",&n,&K);DFT::init();prework();solve();}
}

转载于:https://www.cnblogs.com/yoyoball/p/9362927.html

【2018北京集训(六)】Lcm相关推荐

  1. 北京理工大学计算机学院研究生培养方案,2018北京理工大学非全日制研究生专业学位培养方案...

    [导读]2018北京理工大学非全日制研究生专业学位培养方案包括培养目标.培养方式.学制.课程设置与学分要求.必修环节.培养环节及学位论文相关工作和教学大纲要求等七个部分. 一.培养目标 2018北京理 ...

  2. 2018北京建博会-智能家居展将于3月9日在北京老国展盛大开幕

    2018北京建博会智能家居展距离开展还有28天,欢迎莅临参观! 在"互联网+"的大潮之下,物联网.智能家居行业成为业界的聚焦点,为顺应互联网和人工智能高速发展的大势,满足国民对智能 ...

  3. 北京集训:20180310

    北京集训的第一天,我完美爆零...... 这其中的经历,十分有趣呢. T1: 这题一看就是先猜一个性质然后利用他去求解. 如果我们知道怎么插入,怎么判定的话,可以线段树分治的说. 然后我猜了一个结论: ...

  4. 数学6年级测试软件,苏教版数学六年级下册2018年小学六年级毕业测试试卷(无答案).doc...

    苏教版数学六年级下册2018年小学六年级毕业测试试卷(无答案).doc 文档编号:626490 文档页数:6 上传时间: 2019-04-28 文档级别: 文档类型:doc 文档大小:288.50KB ...

  5. 京东拟申请在北京南六环试点:用无人机送快递

    近日,在WRC 2017(世界机器人大会)上,有媒体透露京东正拟申请在北京南六环周边的大兴部分地区试点无人机送快递. 据北京日报报道,在这次WRC 2017大会上,京东相关负责人表示,他们正在筹备向有 ...

  6. 计算机考试打字题题库6,2018四至六年级电脑测试题

    2018四至六年级电脑测试题 电脑的普及应用,不仅给人们的工作和生活带来了方便和无穷乐趣,也极大地提高了工作的效率.以下是2018四至六年级电脑测试题,欢迎阅读. 一. 单选题 1.一个完整的计算机系 ...

  7. The Open Group 2018 北京峰会召开,看行业大咖都说了哈?

    The Open Group 2018 北京峰会"数字化时代企业变革与人才转型"于8月20日正式拉开帷幕.此次峰会汇集知名企业.国内外顶级专家共同分享数字业务转型.峰会由The O ...

  8. ACM-ICPC 2018 北京赛区网络预赛(A B C D H)

    ACM-ICPC 2018 北京赛区网络预赛(A B C D H) 自闭症患者 A. Saving Tang Monk II 题目链接 题面: 划掉 题意: 大搜索,队友写的 思路: 大搜索,队友写的 ...

  9. 2018北京大兴数字经济论坛-区块链应用创新峰会成功举办

    北京2018年9月26日电 /美通社/ -- 9月21日,"2018北京大兴数字经济论坛•区块链应用创新峰会"在国家新媒体产业基地举办.这是首个在北京范围内由政府指导和发起的区块链 ...

最新文章

  1. 数据结构与算法(8-2)有序表查找(折半查找(二分查找)、插值查找)
  2. libuvc介绍及简单使用
  3. php imagecolorallocate 安装,PHP imagecolorallocate()和imagecolorallocatealpha():定义颜色
  4. 【转载】OmniGraffle (四)化繁为简
  5. Spring.NET 1.3.1 新特性探索系列2——WCF命名空间解析器
  6. 3.15 排列对称串
  7. gitter 卸载_最佳Gitter渠道:PHP
  8. Spark RDD Cache Checkpoint
  9. barmanager 不显示_这个双十一,摄影师的显示器该换了
  10. [转]Http Message结构学习总结
  11. ELK 把date替换为logstash的@timestamp
  12. 高等数学(第七版)同济大学 总习题二 个人解答
  13. interview-db
  14. 把oracle卸载恢复,oracle干净卸载
  15. 视频贴片广告有哪些形式,视频贴片广告展示形式如何?
  16. 为什么管理创新总是发生在汽车行业?
  17. Linux命令:ls -l(ll)结果解析
  18. 快速排序(随机主元)、随机数生成和随机选择算法
  19. 8080端口被占用处理方法
  20. 怎样创建谷歌Merchant Center和链接到购物广告

热门文章

  1. 每天一道LeetCode-----计算二叉树的最大路径和,路径只需要从一个节点到达另一个节点,无其他要求
  2. 记录x86调试命令总结
  3. marc数据个人心得
  4. 二叉树的最大深度—leetcode104
  5. oracle布尔true 1,将.NET布尔数据类型映射到实体框架中的oracle数(1,0)会抛出错误
  6. 机器学习资料整理,收藏了不后悔!
  7. 解决ImportError: cannot import name ‘imread‘ from ‘scipy.misc‘
  8. java sorted排序_【算法】排序算法之计数排序
  9. [C/C++面试题]-错题笔记与解析
  10. python基础入门(Peak带你学python)