【BZOJ4816】数字表格,反演+枚举约数
2017-04-12 08:07
344 查看
传送门
思路:
考场上没怎么卡常,只有60分,感觉自己宛如一个zz
先说一下60分做法
设n≤m
随便化出来∏d=1nf(d)S(⌊nd⌋,⌊md⌋)
其中S(n,m)=∑i=1n∑j=1m[gcd(i,j)=1]=∑d=1nμ(d)⌊nd⌋⌊md⌋
O(T(n+m)34),带有4倍的常数
常数十分优秀的话可以卡过去
正解的想法很像于神之怒加强版(我还做过。。。)
把S带进去
∏d=1nf(d)∑⌊nd⌋k=1μ(k)⌊ndk⌋⌊mdk⌋
如果我们把dk相同的看做一起的话(也就是当做新的变量x),那么实际上就是一个枚举x的约数的过程
∏x=1ng(x)⌊nx⌋⌊mx⌋
其中g(n)=∏d|nf(d)μ(nd)
非常像狄利克雷卷积形式,但并不是,而且这也不是积性函数(显然)
所以用筛倍数的方法求得g(n),复杂度O(nlnn),同时还要处理f(n)的逆元,g(n)的前缀积和逆元
处理询问时的复杂度是O(T(n√+m−−√)logn)
处理逆元时可以O(n)做
思路:
考场上没怎么卡常,只有60分,感觉自己宛如一个zz
先说一下60分做法
设n≤m
随便化出来∏d=1nf(d)S(⌊nd⌋,⌊md⌋)
其中S(n,m)=∑i=1n∑j=1m[gcd(i,j)=1]=∑d=1nμ(d)⌊nd⌋⌊md⌋
O(T(n+m)34),带有4倍的常数
常数十分优秀的话可以卡过去
正解的想法很像于神之怒加强版(我还做过。。。)
把S带进去
∏d=1nf(d)∑⌊nd⌋k=1μ(k)⌊ndk⌋⌊mdk⌋
如果我们把dk相同的看做一起的话(也就是当做新的变量x),那么实际上就是一个枚举x的约数的过程
∏x=1ng(x)⌊nx⌋⌊mx⌋
其中g(n)=∏d|nf(d)μ(nd)
非常像狄利克雷卷积形式,但并不是,而且这也不是积性函数(显然)
所以用筛倍数的方法求得g(n),复杂度O(nlnn),同时还要处理f(n)的逆元,g(n)的前缀积和逆元
处理询问时的复杂度是O(T(n√+m−−√)logn)
处理逆元时可以O(n)做
#include<cstdio> #include<iostream> #define mo 1000000007 using namespace std; int n,m; const int lim=1000000; int prime[lim/10+5],mu[lim+5]; int sf[lim+5],f[lim+5],invf[lim+5],sg[lim+5],g[lim+5],invg[lim+5]; bool vis[lim+5]; int qr(int x,int y) { int t=1; for (;y;y>>=1,x=1LL*x*x%mo) if (y&1) t=1LL*t*x%mo; return t; } void init() { mu[1]=1; for (int i=2;i<=lim;++i) { if (!vis[i]) prime[++prime[0]]=i, mu[i]=-1; for (int j=1;j<=prime[0]&&i*prime[j]<=lim;++j) { vis[i*prime[j]]=1; if (i%prime[j]) mu[i*prime[j]]=-mu[i]; else { mu[i*prime[j]]=0; break; } } } f[0]=0; sf[1]=1; f[1]=1; g[1]=1; sg[1]=1; invg[0]=invg[1]=invf[0]=invf[1]=1; for (int i=2;i<=lim;++i) { f[i]=f[i-1]+f[i-2]; if (f[i]>=mo) f[i]-=mo; sf[i]=1LL*sf[i-1]*f[i]%mo; g[i]=1; } invf[lim]=qr(sf[lim],mo-2); for (int i=lim;i>=2;--i) invf[i-1]=1LL*invf[i]*f[i]%mo, invf[i]=1LL*invf[i]*sf[i-1]%mo; for (int i=2;i<=lim;++i) { for (int j=1;i*j<=lim;++j) if (mu[j]==1) g[i*j]=1LL*g[i*j]*f[i]%mo; else if (mu[j]==-1) g[i*j]=1LL*g[i*j]*invf[i]%mo; sg[i]=1LL*sg[i-1]*g[i]%mo; } invg[lim]=qr(sg[lim],mo-2); for (int i=lim;i>=2;--i) invg[i-1]=1LL*invg[i]*g[i]%mo; } void work() { scanf("%d%d",&n,&m); int t; if (n>m) swap(n,m); int ans=1; for (int last,t,i=1;i<=n;i=last+1) last=min(n/(n/i),m/(m/i)), ans=1LL*ans*qr(1LL*sg[last]*invg[i-1]%mo,1LL*(n/i)*(m/i)%(mo-1))%mo; printf("%d\n",ans); } int main() { init(); int T; for (scanf("%d",&T);T;--T) work(); return 0; }
相关文章推荐
- 【bzoj4816】[Sdoi2017]数字表格 莫比乌斯反演
- [莫比乌斯反演] BZOJ 4816 [Sdoi2017]数字表格
- BZOJ 4816 [Sdoi2017]数字表格 ——莫比乌斯反演
- bzoj 4816: [Sdoi2017]数字表格 莫比乌斯反演
- [数论][莫比乌斯反演] BZOJ 4816: 数字表格
- [数论 反演]BZOJ4816 [Sdoi2017]数字表格
- 【BZOJ4816】【SDOI2017】数字表格 [莫比乌斯反演]
- BZOJ4816 [Sdoi2017]数字表格 【莫比乌斯反演】
- [BZOJ4816][SDOI2017]数字表格(反演)
- BZOJ4816 数字表格-莫比乌斯反演
- 【莫比乌斯反演】BZOJ2154[Crash的数字表格]题解
- [BZOJ4816][SDOI2017]数字表格(莫比乌斯反演)
- 【BZOJ 4816】【SDOI 2017】数字表格
- BZOJ4816 [Sdoi2017]数字表格
- BZOJ 4816 数字表格
- [bzoj4816] [Sdoi2017]数字表格
- [BZOJ4816][SDOI2017]数字表格
- 【BZOJ4816】[Sdoi2017]数字表格 莫比乌斯反演
- 【题解】Crash的数字表格 BZOJ 2154 莫比乌斯反演
- 【bzoj2154】【Crash的数字表格】【莫比乌斯反演】