hdu4746:2013杭州网络赛I 莫比乌斯反演
2015-09-23 21:10
447 查看
题意:
有5000组询问,每组询问求有多少i,j满足i∈[1,n],j∈[1,m]且gcd(i,j)的质因子数目<=p。 n,m<=500000
思路:
首先预处理出每个数的质因子数目分别等于多少,则问题转化为求给定区间内,gcd等于某一堆数的i,j有多少组
发现很像一个基础莫比乌斯反演题:hdu1695。但是此题在某组询问中可能要处理很多个gcd,所以需要进行一些预处理
我们首先筛出每个数的莫比乌斯函数和它的质因子个数
通过容斥的公式可以看出如果要求的gcd为d,那么d*i的倍数对答案的贡献为 mobi[i]。
这样就可以预处理出每个p对应位置的莫比乌斯函数系数之和p[19][500000]
注意这里容易想到p是小于等于18的,因为50W之内的数最多有18个质因子(2^19>500000)
然后对p数组求前缀和,可以使单组查询复杂度变为p*sqrt(n),具体为什么是分块sqrt(n)可以参考hdu1695的题解。交了一发超时了。。
还以为写搓了,后来发现可以再求一次前缀和。。这样单组查询变成了sqrt(n),终于过了
代码:
View Code
有5000组询问,每组询问求有多少i,j满足i∈[1,n],j∈[1,m]且gcd(i,j)的质因子数目<=p。 n,m<=500000
思路:
首先预处理出每个数的质因子数目分别等于多少,则问题转化为求给定区间内,gcd等于某一堆数的i,j有多少组
发现很像一个基础莫比乌斯反演题:hdu1695。但是此题在某组询问中可能要处理很多个gcd,所以需要进行一些预处理
我们首先筛出每个数的莫比乌斯函数和它的质因子个数
通过容斥的公式可以看出如果要求的gcd为d,那么d*i的倍数对答案的贡献为 mobi[i]。
这样就可以预处理出每个p对应位置的莫比乌斯函数系数之和p[19][500000]
注意这里容易想到p是小于等于18的,因为50W之内的数最多有18个质因子(2^19>500000)
然后对p数组求前缀和,可以使单组查询复杂度变为p*sqrt(n),具体为什么是分块sqrt(n)可以参考hdu1695的题解。交了一发超时了。。
还以为写搓了,后来发现可以再求一次前缀和。。这样单组查询变成了sqrt(n),终于过了
代码:
#include <bits/stdc++.h> using namespace std; const int maxn=500000; int mb[maxn+10]; int notprime[maxn+10]; int num[maxn+10]; int prime[maxn]; vector<int>v[20]; int np=0; void setprime() { memset(notprime,0,sizeof(notprime)); mb[1]=1; num[1]=0; for(int i=2; i<=maxn; i++) { if(!notprime[i]) { prime[np++]=i; num[i]=1; mb[i]=-1; } for(int j=0; j<np&&i*prime[j]<=maxn; j++) { notprime[i*prime[j]]=1; num[i*prime[j]]=num[i]+1; //v[num[i]+1].push_back(i*prime[j]); if(i%prime[j]==0) { mb[i*prime[j]]=0; break; } else { mb[i*prime[j]]=-mb[i]; } } } } int p[20][500050]; int sum[20][500050]; int f[20][500050]; int main() { freopen("in.txt","r",stdin); setprime(); for(int i=1; i<=maxn; i++) { for(int j=1; i*j<=maxn; j++) { p[num[i]][i*j]+=mb[j]; } } for(int i=0; i<=18; i++) { sum[i][0]=0; for(int j=1; j<=maxn; j++) { sum[i][j]=sum[i][j-1]+p[i][j]; } } for(int i=1; i<=maxn; i++) { f[0][i]=sum[0][i]; for(int j=1;j<=18;j++) { f[j][i]=f[j-1][i]+sum[j][i]; } } int n,m,k,q; scanf("%d",&q); while(q--) { long long ans=0; scanf("%d%d%d",&n,&m,&k); if(k>18) { printf("%I64d\n",(long long)n*m); continue; } if(n>m) swap(n,m); for(int j=1; j<=n; j++) { int to=min(n/(n/j),m/(m/j)); ans+=(long long)(n/j)*(m/j)*(f[k][to]-f[k][j-1]); j=to; } printf("%I64d\n",ans); } return 0; }
View Code
相关文章推荐
- iOS开发中的网络请求
- 交叉线还是直通线?
- 多线程
- 关于超文本传送协议HTTP的小小总结
- c#网络编程学习笔记00_补上一些基础概念
- iOS:网络编程中三个数据解析协议HTTP、XML、JSON的详细介绍
- iOS_网络编程
- IOS开发-第三方SDWebImage下载网络图片的使用
- Android中 Http请求
- 陈硕 - Linux 多线程服务端编程 - muduo 网络库作者
- uva 10054 The Necklace(欧拉回路)
- 网络字节序和主机字节序
- [网络流] HDOJ 5457 Hold Your Hand
- 第十四章:网络编程
- RTCP
- HTTP POST GET 本质区别详解
- 高性能网络编程7--tcp连接的内存使用
- centosxxx + redhat5.5 快速设置网络源
- 安卓Imageview控件如何获取网络图片
- TCP/IP协议三次握手与四次握手流程解析