luogu2257 YY的GCD
答案为:
(sumlimits_{pin prime}sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}[gcd(i,j)==p])
(=sumlimits_{pin prime}sumlimits_{i=1}^{lfloorfrac{n}{p}
floor}sumlimits_{j=1}^{{lfloorfrac{m}{p}}
floor}[gcd(i,j)==1])
(=sumlimits_{pin prime}sumlimits_{i=1}^{lfloorfrac{n}{p}
floor}sumlimits_{j=1}^{{lfloorfrac{m}{p}}
floor}sumlimits_{t mid iland tmid j}mu(t))
(=sumlimits_{pin prime}sumlimits_{t=1}^{min(n,m)}mu(t)sumlimits_{i=1}^{lfloorfrac{n}{pt}
floor}sumlimits_{j=1}^{{lfloorfrac{m}{pt}}
floor}1)
(=sumlimits_{pin prime}sumlimits_{t=1}^{min(n,m)}mu(t)lfloorfrac{n}{pt}
floorlfloorfrac{m}{pt}
floor)
令(T=pt)。
原式(=sumlimits_{pin prime}sumlimits_{t=1}^{min(n,m)}mu(t)lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floor)
(=sumlimits_{T=1}^{min(n,m)}lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floorsumlimits_{pin prime land pmid T}mu(frac{T}{p}))
前面的(sumlimits_{T=1}^{min(n,m)}lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floor)就是一个整除分块。这就需要我们求出(sumlimits_{pin prime land pmid T}mu(frac{T}{p}))的前缀合,我们枚举p然后遍历值域就是调和级数复杂度的预处理。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int mu[10000002],prim[1000000],vis[10000002],g[10000002],sum[10000002],cnt,n[10100],m[10100],t;
long long ans;
inline void read(int &x)
{
x=0;
static int p;p=1;
static char c;c=getchar();
while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
x*=p;
}
void getmu(int n){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
prim[++cnt]=i;
mu[i]=-1;
}
for(int j=1;i*prim[j]<=n;j++){
vis[i*prim[j]]=1;
if(i%prim[j]==0)break;
else mu[i*prim[j]]=-mu[i];
}
}
for(int i=1;i<=cnt;i++)
for(int j=1;j*prim[i]<=n;j++)
g[j*prim[i]]+=mu[j];
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+g[i];
}
int main(){
read(t);
int tot=0;
for(int i=1;i<=t;i++){
read(n[i]);read(m[i]);
tot=max(tot,max(n[i],m[i]));
}
getmu(tot);
for(int i=1;i<=t;i++){
ans=0;
if(n[i]>m[i])swap(n[i],m[i]);
for(int l=1,r;l<=n[i];l=r+1){
r=min(n[i]/(n[i]/l),m[i]/(m[i]/l));
ans+=(long long)(n[i]/l)*(m[i]/l)*(sum[r]-sum[l-1]);
}
printf("%lld
",ans);
}
return 0;
}
[CQOI2015]选数
显然是一道莫比乌斯反演。
设f[i]为选出n个数gcd为i的方案数。
设F[i]为选出n个数gcd为i的倍数的方案数。
把L,H都除去k,答案就是f[1](注意一点除的时候当L%k!=0是L除完k之后还要加1)。
然后有(F[i]=sumlimits_{i mid d }f[d])
(f[i]=sumlimits_{i mid d} mu(frac{d}{i})F[d])
应为我们要求的是f[1],
(f[1]=sumlimits_d mu(d)F[d])
F怎么求就是可以选的数的n次方:((lfloorfrac{H}{k}
floor-lfloorfrac{L-1}{k}
floor)^n)
(mu)前缀和用杜教筛就行了。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<map>
using namespace std;
#define int long long
map<int,bool>vis;
map<int,int> hsh;
const int N=2001000;
const int p=1e9+7;
int mu[N],book[N],prime[N],cnt,n,k,L,H;
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
void pre_work(){
mu[1]=1;
for(int i=2;i<=1000000;i++){
if(book[i]==0){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
book[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int sum(int x){
if(x<=1000000)return mu[x];
if(vis[x]==1)return hsh[x];
int tmp=1;
for(int l=2,r;l<=x;l=r+1){
r=(x/(x/l));
tmp=tmp-(r-l+1ll)*sum(x/l)%p;
tmp=(tmp%p+p)%p;
}
vis[x]=1;hsh[x]=tmp;
return tmp;
}
int ksm(int x,int b){
int tmp=1;
while(b){
if(b&1)tmp=tmp*x%p;
b>>=1;
x=x*x%p;
}
return tmp;
}
int work(){
int tmp=0;
for(int l=1,r;l<=H;l=r+1){
if(l>L-1ll)r=H/(H/l);
else r=min(H/(H/l),(L-1ll)/((L-1ll)/l));
tmp=tmp+(sum(r)-sum(l-1ll))*ksm((H/l-(L-1ll)/l),n)%p;
tmp=(tmp%p+p)%p;
}
return tmp;
}
signed main(){
pre_work();
n=read();k=read();L=read();H=read();
if(L%k)L=L/k+1;
else L=L/k;
H=H/k;
printf("%lld",work());
return 0;
}
BZOJ 4176 Lucas的数论
((n leq 1e9))f代表约数个数。
(f(ij)=sumlimits_{x mid i }sumlimits_{ ymid j}[gcd(x,y)==1])
我有一个精巧的证明可惜这里地方太小,写不下。
(我才知道这是费马说的,思维风暴证明费马大定理?)
我才没有什么精巧的证明,我会暴力。
因为约束个数函数是极性函数,把每一个质因子分开考虑。
设ij中有(p^k),i中有(p^a),j中有(p^b),其中a+b=k。
然后我们惊奇的发现(p^k)的约数个数为k+1=a+b+1。然后(p^a),和(p^b)互质的情况有a+b+1。
然后就可以嘿嘿嘿了。
原式等于:(sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}sumlimits_{x mid i }sumlimits_{ ymid j}[gcd(x,y)==1])
(=sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}sumlimits_{x mid i }sumlimits_{ymid j}sumlimits_{tmid xland tmid y}mu(t))
(=sumlimits_{t=1}^{n}mu(t)sumlimits_{x=1}^{lfloorfrac{n}{t}
floor}lfloorfrac{n}{tx}
floorsumlimits_{y=1}^{lfloorfrac{n}{t}
floor}lfloorfrac{n}{ty}
floor)
(=sumlimits_{t=1}^{n}mu(t) (sumlimits_{x=1}^{
lfloor frac{n}{t}
floor} lfloorfrac{n}{tx}
floor)^2)
然后上整除分块。
复杂度。。听说是(O(sqrt{n}logsqrt{n}))
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
mu[1]=1;
for(int i=2;i<=1000000;i++){
if(book[i]==0){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
book[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
int tmp=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
tmp=(tmp+(r-l+1)*(x/l)%p)%p;
}
return tmp;
}
int sum(int x){
if(x<=1000000)return mu[x];
if(vis[x])return hsh[x];
int tmp=1;
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
tmp=tmp-sum(x/l)*(r-l+1);
tmp=(tmp%p+p)%p;
}
vis[x]=1;hsh[x]=tmp;
return tmp;
}
int calc(int x){
int tmp=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
int w=work(x/l);
tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
tmp=(tmp%p+p)%p;
}
return tmp;
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
signed main(){
n=read();
pre_work();
printf("%lld",calc(n));
return 0;
}
luogu P3768 简单的数学题
(sumlimits_{i=1}^{n}sumlimits_{j=1}^{m}ijgcd(i,j))
gcd不仅可以化成(mu)
原式等于
(sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}ijsumlimits_{t mid i land t mid j} varphi(t))
为什么?
因为(varphi*1)=id
然后接着化简。
(sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}ijsumlimits_{t mid i land t mid j} varphi(t))
(=sumlimits_{t=1}^{n}varphi(t)*t^2sumlimits_{i=1}^{lfloorfrac{n}{t}
floor}sumlimits_{j=1}^{lfloorfrac{n}{t}
floor}ij)
(=sumlimits_{t=1}^{n}varphi(t)*t^2(sumlimits_{i=1}^{lfloorfrac{n}{t}
floor}i)^2)
(=sumlimits_{t=1}^{n}varphi(t)*t^2sumlimits_{i=1}^{lfloorfrac{n}{t}
floor}i^3)
然后就可以做了。
最后一步怎么化简?暴力展开。。。
然后上杜教筛就行了。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
#define int long long
const int p=1e9+7;
const int N=1010000;
map<int,int>hsh;
map<int,bool>vis;
int mu[N],book[N],prime[N],cnt,n;
void pre_work(){
mu[1]=1;
for(int i=2;i<=1000000;i++){
if(book[i]==0){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&prime[j]*i<=1000000;j++){
book[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=1000000;i++)mu[i]+=mu[i-1];
}
int work(int x){
int tmp=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
tmp=(tmp+(r-l+1)*(x/l)%p)%p;
}
return tmp;
}
int sum(int x){
if(x<=1000000)return mu[x];
if(vis[x])return hsh[x];
int tmp=1;
for(int l=2,r;l<=x;l=r+1){
r=x/(x/l);
tmp=tmp-sum(x/l)*(r-l+1);
tmp=(tmp%p+p)%p;
}
vis[x]=1;hsh[x]=tmp;
return tmp;
}
int calc(int x){
int tmp=0;
for(int l=1,r;l<=x;l=r+1){
r=x/(x/l);
int w=work(x/l);
tmp=tmp+(sum(r)-sum(l-1))*w%p*w%p;
tmp=(tmp%p+p)%p;
}
return tmp;
}
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
signed main(){
n=read();
pre_work();
printf("%lld",calc(n));
return 0;
}
[SDOI2017]数字表格
(Pi_{i=1}^{n}Pi_{j=1}^{m}f[gcd(i,j)])
(=Pi_{d=1} f[d]^{sumlimits_{i=1}^{lfloorfrac{n}{d}
floor}sumlimits_{j=1}^{lfloorfrac{n}{d}
floor}[gcd(i,j)==1]})
(=Pi_{d=1} f[d]^{sumlimits_{i=1}^{lfloorfrac{n}{d}
floor}sumlimits_{j=1}^{lfloorfrac{n}{d}
floor}sumlimits_{t mid iland tmid j}mu(t)})
(=Pi_{d=1} f[d]^{sumlimits_{i=1}^{min(n,m)}mu(t)lfloorfrac{n}{dt}
floorlfloorfrac{m}{dt}
floor})
令T=dt
(=Pi_{T=1}^{min(m,n)}Pi_{dmid T} f[d]^{mu(frac{T}{d})lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floor})
然后我们把(lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floor)分块。(Pi_{dmid T} f[d]^{mu(frac{T}{d})})预处理。因为调和级数复杂度可以接受。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1010100;
int mu[N],book[N],prime[N],cnt,f[N],T,n,m,F[N],inv[N];
int read(){
int sum=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return sum*f;
}
int ksm(int x,int b){
int tmp=1;
while(b){
if(b&1)tmp=tmp*x%mod;
b>>=1;
x=x*x%mod;
}
return tmp;
}
void pre_work(){
mu[1]=1;
for(int i=2;i<=1000000;i++){
if(book[i]==0){
mu[i]=-1;
prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<=1000000;j++){
book[i*prime[j]]=1;
if(i%prime[j]==0)break;
mu[i*prime[j]]=-mu[i];
}
}
f[0]=0;f[1]=1;
for(int i=2;i<=1000000;i++)
f[i]=(f[i-1]+f[i-2])%mod;
for(int i=0;i<=1000000;i++)F[i]=1;
for(int i=1;i<=1000000;i++)
for(int j=1;j*i<=1000000;j++){
int tmp;
if(mu[j]==0)tmp=1;
else if(mu[j]==1)tmp=f[i];
else tmp=ksm(f[i],mod-2);
F[i*j]=(F[i*j]*tmp)%mod;
}
for(int i=2;i<=1000000;i++)F[i]=(F[i-1]*F[i])%mod;
for(int i=1;i<=1000000;i++)inv[i]=ksm(F[i],mod-2);
inv[0]=1;
}
int work(int n,int m){
if(n>m)swap(n,m);
int tmp=1;
for(int l=1,r;l<=n;l=r+1){
r=min(m/(m/l),n/(n/l));
tmp=(tmp*ksm(F[r]*inv[l-1]%mod,(n/l)*(m/l)))%mod;
}
return tmp;
}
signed main(){
T=read();
pre_work();
while(T--){
n=read(),m=read();
printf("%lld
",work(n,m));
}
return 0;
}