博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2019ICPC西安邀请赛 - B. Product - 数论
阅读量:5327 次
发布时间:2019-06-14

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

打印的时候麻烦把:。

\(\prod\limits_{i=1}^{n} \prod\limits_{j=1}^{n} \prod\limits_{k=1}^{n} m^{gcd(i,j)[k|gcd(i,j)]} mod p\)

欧拉定理:

\(m^{ \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \sum\limits_{k=1}^{n} {gcd(i,j)[k|gcd(i,j)]} mod (p-1) }mod p\)

算出指数直接套快速幂就可以了。考虑指数怎么算。为了方便把取模省略。

$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \sum\limits_{k=1}^{n} {gcd(i,j)[k|gcd(i,j)]} $

这种带个整除的,一般都是交换求和到前面,然后后面算他的倍数。

$=\sum\limits_{k=1}^{n} \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} {gcd(i,j)[k|gcd(i,j)]} $

带方括号的,枚举g,去括号。把g放在前面。

$=\sum\limits_{g=1}^{n}g\sum\limits_{k=1}^{n} \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} {[gcd(i,j)==g][k|g]} $

k与i,j无关了,提到前面。

$=\sum\limits_{g=1}^{n}g\sum\limits_{k=1}^{n} [k|g] \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} {[gcd(i,j)==g]} $

那么这个k就是d(g)了。

$=\sum\limits_{g=1}^{n}g*d(g) \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} {[gcd(i,j)==g]} $

后面那个就把g提出来,好像还是很套路的。在什么鬼GCD统计里见过下式:

$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} {[gcd(i,j)==g]} $
$ = \sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor} \sum\limits_{j=1}^{\lfloor\frac{n}{g}\rfloor} {[gcd(i,j)==1]} $

那么这个两个数的互质对,规定i比j大于等于,那么就是欧拉函数,特别的,\(\varphi(1)=1\)。(i,j)和(j,i)互换重复计算(1,1)。

$ = \sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor} \sum\limits_{j=1}^{\lfloor\frac{n}{g}\rfloor} {[gcd(i,j)==1]} $
$ = 2 ( \sum\limits_{i=1}^{\lfloor\frac{n}{g}\rfloor} \varphi(i) ) - 1 $

这个东西用杜教筛板子都不用思考任何东西,直接弄出来(说起来我的杜教筛好像多个log)。

记上面的算式为 \(S(\lfloor\frac{n}{g}\rfloor)\) ,那么原来的简化为:

$=\sum\limits_{g=1}^{n}g*d(g) S(\lfloor\frac{n}{g}\rfloor) $

换个喜欢的字母!!!

$=\sum\limits_{i=1}^{n}i*d(i) S(\lfloor\frac{n}{i}\rfloor) $

这些都跟g有关,太自闭了,大半时间卡在这里。后面的函数明显可以分块到 \(O(\sqrt{n})\),对于一段连续的i求出结果。

要是可以计算前面的 \(\sum\limits_{i=1}^{n}i*d(i)\) 的前缀和,那么可以再花\(O(1)\)求出一段连续的i的求和结果和后面相乘。

最后的问题就是怎么快速计算 \(\sum\limits_{i=1}^{n}i*d(i)\) 这个狗东西。

首先可以线性预处理到5e6左右,花费一大堆内存,因为d(i)我是有线性筛的(虽然不会他的杜教筛)。

(菜鸡的)第一次重要的突破!

每个数的贡献和他的因子个数成线性。
然后我突发奇想,枚举每个因子d,枚举到 \(\sqrt{n}\) ,每个因子会使得他的每个倍数都恰好多贡献1倍,那么就是 \(d+2d+3d+...+\lfloor\frac{n}{d}\rfloor * d\)
等差数列求和嘛!
那么就变成:
\(\sum\limits_{i=1}^{n}i*d(i)\)
\(=\sum\limits_{d=1}^{\lfloor\sqrt{n}\rfloor} \frac{d*(1+\lfloor\frac{n}{d}\rfloor)*(\lfloor\frac{n}{d}\rfloor)}{2}\)

已经变成 \(O(\sqrt{n})\)求的了,当时出不了第三个样例,怎么想也是哦,能出就奇怪了。

(菜鸡的)第二次重要的突破!

233,看了半天才发现,上面不是也可以分块求的吗?对\(\lfloor\frac{n}{d}\rfloor\)分块啊。对一段连续的d,\(\lfloor\frac{n}{d}\rfloor\)都是同一个值,想了半天才领会!
那么就可以 \(O(n^{\frac{1}{4}})\) 求出\(\sum\limits_{i=1}^{n}i*d(i)\) 了,把这个狗东西记为 \(T(i)\)


$=\sum\limits_{i=1}^{n}T(i) S(\lfloor\frac{n}{i}\rfloor) $

杜教筛预处理 \(O(n^{\frac{2}{3}})\) ,单次询问S(x)差不多 \(O(1)\) ,虽然我的杜教筛比dq的多了一点东西。

单次询问T(i)差不多 \(O(n^{\frac{1}{4}})\) 。意思是对单个i,处理出答案需要 \(O(n^{\frac{1}{4}})\)

求和时,第一次分块对i分块用了 \(O(\sqrt{n})\),每段连续的i共用一个T(i) S(\lfloor\frac{n}{i}\rfloor) ,所以就是 \(O(n^{\frac{3}{4}})\) 。n是1e9,刚刚好够。


其他注意点:

1.应该用欧拉定理,这里卡了很久!幂取模不是直接取的,不要搞假算法!

2.涉及除法的运算,恰好求和公式可以约掉那个2,不要对那个数字取模!


总结:

\(\sum\limits_{i=1}^{n}i*d(i)=\sum\limits_{d=1}^{\lfloor\sqrt{n}\rfloor} \frac{d*(1+\lfloor\frac{n}{d}\rfloor)*(\lfloor\frac{n}{d}\rfloor)}{2}\) 可以 \(O(n^{\frac{1}{4}})\) 计算。
同理可得 \(\sum\limits_{i=1}^{n}d(i)=\sum\limits_{d=1}^{\lfloor\sqrt{n}\rfloor} \frac{(1+\lfloor\frac{n}{d}\rfloor)*(\lfloor\frac{n}{d}\rfloor)}{2}\) 也是一样的。
说白了是我不会计算\(d(i)\)前缀和的方法,过于依赖线性筛了,去关注一下单个xx函数的求法吧。


当时写的代码,270分钟1A,好像挤到金银分隔线。

#include
using namespace std;typedef long long ll;const int MAXN=5e6;ll n,m,p,mod;ll inv2;ll qpow(ll x,ll n){ ll res=1%p; while(n) { if(n&1) res=(res*x)%p; x=(x*x)%p; n>>=1; } return res%p;}ll Qarray[MAXN+5];//Q(n)=\sum_i=1^n i*d(i)inline ll Q(ll n){ if(n<=MAXN) return Qarray[n]; ll res=0; for(ll l=1,r;l<=n; l=r+1){ r=n/(n/l); ll t=n/l; ll tmp1=((t+1)*t/2)%mod; ll tmp2=((l+r)*(r-l+1)/2)%mod; res=(res+(tmp1*tmp2%mod))%mod; } return res%mod;}unordered_map
Sphi;ll sump[MAXN+5];int pri[MAXN+5],pritop;bool notpri[MAXN+5];ll d[MAXN+5],a[MAXN+5];void sieve(int n=MAXN){ pritop=0; notpri[1]=1; sump[1]=1; d[1]=1; for(int i=2; i<=n; i++) { if(!notpri[i]) { pri[++pritop]=i; d[i]=2; a[i]=1; sump[i]=(i-1)%mod; } for(int j=1; j<=pritop&&i*pri[j]<=n; j++) { notpri[i*pri[j]]=1; if(i%pri[j]) { d[i*pri[j]]=d[i]*d[pri[j]]; a[i*pri[j]]=1; sump[i*pri[j]]=sump[i]*sump[pri[j]]%mod; } else { d[i*pri[j]]=d[i]/(a[i]+1)*(a[i]+2); a[i*pri[j]]=a[i]+1; sump[i*pri[j]]=sump[i]*pri[j]%mod; break; } } } Qarray[0]=0; Qarray[1]=1; for(int i=2; i<=n; i++) { sump[i]=(sump[i-1]+sump[i])%mod; Qarray[i]=(Qarray[i-1]+1ll*i*d[i])%mod; } return;}inline ll GetSphi(int n){ if(n<=MAXN) return sump[n]; if(Sphi.count(n)) return Sphi[n]; ll ret=n; if(ret%2==0) ret=(ret/2)*(1ll+n); else ret*=(1ll+n)/2; for(ll l=2,r;l<=n; l=r+1) { ll t=n/l; r=n/(t); ret-=(r-l+1)*GetSphi(t); } ret%=mod; return Sphi[n]=ret;}int main(){#ifdef local freopen("Yinku.in","r",stdin);#endif // local scanf("%lld%lld%lld",&n,&m,&p); mod=p-1; sieve(); ll ans=0; for(ll l=1,r; l<=n; l=r+1) { ll t=n/l; r=n/t; ll tmp=(Q(r)-Q(l-1)+mod)%mod; ll tmp2=GetSphi(t)%mod; ans=(ans+tmp*tmp2%mod)%mod; } //printf("ans=%lld\n",ans); ans=(ans*2ll)%mod; ans=(ans-Q(n)+mod)%mod; //printf("ans=%lld\n",ans); ll Ans=qpow(m,ans%mod); printf("%lld\n",Ans); return 0;}

转载于:https://www.cnblogs.com/Yinku/p/10963374.html

你可能感兴趣的文章
Redis常用命令
查看>>
2018.11.06 bzoj1040: [ZJOI2008]骑士(树形dp)
查看>>
2019.02.15 bzoj5210: 最大连通子块和(链分治+ddp)
查看>>
redis cluster 集群资料
查看>>
微软职位内部推荐-Sr. SE - Office incubation
查看>>
微软职位内部推荐-SOFTWARE ENGINEER II
查看>>
centos系统python2.7更新到3.5
查看>>
C#类与结构体究竟谁快——各种函数调用模式速度评测
查看>>
我到底要选择一种什么样的生活方式,度过这一辈子呢:人生自由与职业发展方向(下)...
查看>>
poj 题目分类
查看>>
windows 安装yaml支持和pytest支持等
查看>>
读书笔记:季羡林关于如何做研究学问的心得
查看>>
面向对象的优点
查看>>
套接口和I/O通信
查看>>
阿里巴巴面试之利用两个int值实现读写锁
查看>>
浅谈性能测试
查看>>
Winform 菜单和工具栏控件
查看>>
CDH版本大数据集群下搭建的Hue详细启动步骤(图文详解)
查看>>
巧用Win+R
查看>>
浅析原生js模仿addclass和removeclass
查看>>