bzoj 3309 DZY Loves Math
2015-09-04 21:28
302 查看
记录一下思路,蛮好的数论+筛题,顺便熟悉一下数学公式
∑i=1a∑j=1bf(gcd(i,j))=∑d∑i=1a/d∑j=1b/df(d)⋅[gcd(i,j)=1]=∑df(d)⋅∑i=1a/d∑j=1b/d∑x∣gcd(i,j)μ(x)=∑df(d)⋅∑xμ(x)⋅adx⋅bdx=∑DaD⋅bD⋅∑x∣Df(x)⋅μ(D/x)\begin{align*}
\sum_{i=1}^a \sum_{j=1}^b f(gcd(i,j))&=\sum_d\sum_{i=1}^{a/d}\sum_{j=1}^{b/d}f(d)\cdot[gcd(i,j)=1] \\
&= \sum_d f(d)\cdot\sum_{i=1}^{a/d}\sum_{j=1}^{b/d}\sum_{x\mid gcd(i,j)}\mu(x) \\
&= \sum_df(d)\cdot\sum_x\mu(x)\cdot\dfrac{a}{dx}\cdot\dfrac{b}{dx}\\
&=\sum_D\dfrac{a}{D}\cdot\dfrac{b}{D}\cdot\sum_{x\mid D}f(x)\cdot\mu(D/x)\\
\end{align*}
我们令
g(n)=∑x∣nf(n/x)⋅μ(x)g(n)=\sum_{x\mid n}f(n/x)\cdot\mu(x)
这里的形式称为Dirichlet卷积
可以用O(nlog(n))O(nlog(n))的复杂度求出来
但是题目要求我们用线性的时间算出gg.
问题变成了如何高效的求出g(n)g(n),不失一般性,我们首先来看看n=pkn=p^k,只有当x=px=p和x=1x=1时,μ\mu函数不等于0,所以这种情况下g(n)=1g(n)=1,注意,规定μ(1)=1\mu(1)=1。当nn的素因子不止一个的时候,假设不能产生ff的素因子组成的集合为AA,由于在这个集合里取值的个数会改变整体μ\mu函数的正负性,且正好符合(1−1)|A|(1-1)^{|A|}的二项式展开式,所以总体为0,因此如果nn的质因子分解中两个不同素数的幂不一样,那么g(n)=0g(n)=0,接下来我们考虑所有质因子的幂次都相同的情况,因为对nn的每一个质因子,μ\mu函数决定了xx只能取一个或者不去,当全部取走的时候f(n/x)=k−1f(n/x)=k-1,假设幂次为k,分解出的质因子个数为num,否则的话f(n/x)=kf(n/x)=k,用数学公式表示出来如下∑i=0num−1(numi)⋅k⋅(−1)i+(k−1)⋅(−1)num=(−1)num+1\sum_{i=0}^{num-1}\binom{num}{i}\cdot k\cdot (-1)^i+(k-1)\cdot(-1)^num =(-1)^{num+1}
所以我们需要用线性筛判断数nn的所有质因子的幂是否相等,回忆线性筛,我们用nn的最小素因子晒去nn,所以我们记录最小素因子的幂次即可。
然而还没有完。。。
我们需要O(n−−√)O(\sqrt{n})的复杂度回答询问,这里利用了n/xn/x的取值只有O(n−−√)O(\sqrt{n})数量级的性质,所以预处理出gg的前缀和以便计算,由于这个问题非常经典,下面给出代码。
题目代码
外日,真TMD的累,突然对写博客的人产生了敬佩感。。。尤其是写数学公式的。。。
∑i=1a∑j=1bf(gcd(i,j))=∑d∑i=1a/d∑j=1b/df(d)⋅[gcd(i,j)=1]=∑df(d)⋅∑i=1a/d∑j=1b/d∑x∣gcd(i,j)μ(x)=∑df(d)⋅∑xμ(x)⋅adx⋅bdx=∑DaD⋅bD⋅∑x∣Df(x)⋅μ(D/x)\begin{align*}
\sum_{i=1}^a \sum_{j=1}^b f(gcd(i,j))&=\sum_d\sum_{i=1}^{a/d}\sum_{j=1}^{b/d}f(d)\cdot[gcd(i,j)=1] \\
&= \sum_d f(d)\cdot\sum_{i=1}^{a/d}\sum_{j=1}^{b/d}\sum_{x\mid gcd(i,j)}\mu(x) \\
&= \sum_df(d)\cdot\sum_x\mu(x)\cdot\dfrac{a}{dx}\cdot\dfrac{b}{dx}\\
&=\sum_D\dfrac{a}{D}\cdot\dfrac{b}{D}\cdot\sum_{x\mid D}f(x)\cdot\mu(D/x)\\
\end{align*}
我们令
g(n)=∑x∣nf(n/x)⋅μ(x)g(n)=\sum_{x\mid n}f(n/x)\cdot\mu(x)
这里的形式称为Dirichlet卷积
可以用O(nlog(n))O(nlog(n))的复杂度求出来
int f[MAXN],mu[MAXN],g[MAXN]={0}; void calc(int n) { for (int i=1;i*i<=n;i++) for (int j=i;i*j<=n;j++) if(j==i)g[i*j]+=f[i]*mu[i]; else g[i*j]+=f[i]*mu[j]+f[j]*mu[i]; }
但是题目要求我们用线性的时间算出gg.
问题变成了如何高效的求出g(n)g(n),不失一般性,我们首先来看看n=pkn=p^k,只有当x=px=p和x=1x=1时,μ\mu函数不等于0,所以这种情况下g(n)=1g(n)=1,注意,规定μ(1)=1\mu(1)=1。当nn的素因子不止一个的时候,假设不能产生ff的素因子组成的集合为AA,由于在这个集合里取值的个数会改变整体μ\mu函数的正负性,且正好符合(1−1)|A|(1-1)^{|A|}的二项式展开式,所以总体为0,因此如果nn的质因子分解中两个不同素数的幂不一样,那么g(n)=0g(n)=0,接下来我们考虑所有质因子的幂次都相同的情况,因为对nn的每一个质因子,μ\mu函数决定了xx只能取一个或者不去,当全部取走的时候f(n/x)=k−1f(n/x)=k-1,假设幂次为k,分解出的质因子个数为num,否则的话f(n/x)=kf(n/x)=k,用数学公式表示出来如下∑i=0num−1(numi)⋅k⋅(−1)i+(k−1)⋅(−1)num=(−1)num+1\sum_{i=0}^{num-1}\binom{num}{i}\cdot k\cdot (-1)^i+(k-1)\cdot(-1)^num =(-1)^{num+1}
所以我们需要用线性筛判断数nn的所有质因子的幂是否相等,回忆线性筛,我们用nn的最小素因子晒去nn,所以我们记录最小素因子的幂次即可。
然而还没有完。。。
我们需要O(n−−√)O(\sqrt{n})的复杂度回答询问,这里利用了n/xn/x的取值只有O(n−−√)O(\sqrt{n})数量级的性质,所以预处理出gg的前缀和以便计算,由于这个问题非常经典,下面给出代码。
for(int i=1,j;i<=n;i=j+1){ j=min(n/(n/i),m/(m/i)); ans+=(f[j]-f[i-1])*(Sum(n/i,m/i)); }
题目代码
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <time.h> #include <cmath> using namespace std; #define N 10000000 #define inf 0x3f3f3f3f #define mod 1000000007 typedef long long ll; int num[N+10],g[N+10],prime[N+10],pre[N+10]; bool check[N+10]; void init() { memset(check,false,sizeof(check)); int tot=0; for(int i=2;i<=N;i++) { if(!check[i]){ prime[tot++]=i; num[i]=1; g[i]=1; pre[i]=1; } for(int j=0;j<tot;j++) { if((ll)i*prime[j]>N) break; check[i*prime[j]]=1; if(i%prime[j]==0) { num[i*prime[j]]=num[i]+1; if(pre[i]==1||(num[i*prime[j]]==num[pre[i]])) g[i*prime[j]]= (pre[i]==1)?1:-g[pre[i]]; else g[i*prime[j]]=0; pre[i*prime[j]]=pre[i]; } else{ num[i*prime[j]]=1; if(num[i]==1) g[i*prime[j]]=-g[i]; else g[i*prime[j]]=0; pre[i*prime[j]]=i; } } } g[1]=0; for(int i=2;i<=N;i++) g[i]+=g[i-1]; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T,a,b,pos; ll ans; init(); scanf("%d",&T); while(T--) { scanf("%d%d",&a,&b); if(a>b) swap(a,b); ans=0; for(int i=1;i<=a;i=pos+1) { pos=min(a/(a/i),b/(b/i)); ans+=(ll)(a/i)*(b/i)*(g[pos]-g[i-1]); } printf("%lld\n",ans); } return 0; }
外日,真TMD的累,突然对写博客的人产生了敬佩感。。。尤其是写数学公式的。。。
相关文章推荐
- 将vim初步配置可以编译的编辑器(for windows)
- 欧拉工程第63题:Powerful digit counts
- C语言字符串操作函数整理
- [HDU 2692] Ball · 二分答案+最短路
- 10622 - Perfect P-th Powers(唯一分解定律)
- 漫谈理想
- 关于磁盘缓存与磁盘缓冲区的理解与延伸
- JQ简单案例
- KMP算法
- 快捷键
- 5道经典的程序题 (4)
- wamp2.5(Apache2.4.9+PHP5.5.12)关于多站点的配置
- android 自定义属性
- UI控件圆角设置
- Linux(Ubuntu)下面SecureCRT 完全破解
- 敬业与乐业
- 学生管理系统——基于双向循环链表
- 使用testlink测试工具遇到的问题及解决方法
- matlab保存图片
- 3.5-Mysql备份与恢复