莫反练习题
呃,其实最重要的步骤就几个:
-
看到类似 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\gcd(i,j)\) 这类计数就转化为枚举 \(\gcd(i,j)=k\),后面就是 \(k[\gcd(i,j)=k]\),目的是弄出来这个 \([\gcd(i,j)=k]\)。
-
转化为 \([\gcd(i,j)=1]\)。
-
转化为 \(\sum\limits_{d\mid \gcd(i,j)}\mu(d)\)。
-
枚举 \(i,j\) 转化为枚举 \(d\)。
-
如果外面还有一层,即内层为 \(\lfloor\dfrac{n}{pd}\rfloor\) 形式的,令 \(T=pd\),然后枚举 \(pd\) 以及 \(d\mid T\)。
推导过程如果没有写 \(\min(n,m)\) 都默认 \(n<m\)。
「HAOI2011」Problem b
前置:\([\gcd(i,j)=1]=\sum\limits_{d\mid \gcd(i,j)}\mu(d)\)。
考虑容斥,设 \(f(l,r)\) 为 \(x\in [1,l],y\in [1,r]\) 的答案。
则 \(ans=f(b,d)-f(a,d)-f(b,c)+f(a,c)\)。
考虑如何求解 \(f\)。
改为枚举 \(\frac{i}{k}\) 和 \(\frac{j}{k}\):
改为枚举 \(d\),范围显然是 \([1,\lfloor\dfrac{\min(n,m)}{k}\rfloor]\),相当于对于每个 \(d\),求有多少对 \(i,j\) 满足 \(d\mid \gcd(i,j)\),即 \(d\mid i\) 且 \(d\mid j\)。显然,\([1,\lfloor\dfrac{n}{k}\rfloor]\) 范围内是 \(d\) 的倍数的数有 \(\lfloor\dfrac{n}{dk}\rfloor\) 个:
数论分块即可。
#include<bits/stdc++.h>
#define sd std::
//#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define f(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e5+10;
sd vector<int> pr;
int p[N],mu[N],sum[N];
void pre(int n)
{
mu[1]=1;p[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(it*i>n) break;
p[i*it]=1;
if(i%it==0)
{
mu[it*i]=0;
break;
}
else mu[it*i]=-mu[i];
}
}
F(i,1,n) sum[i]=sum[i-1]+mu[i];
}
int ask(int l,int r)
{
return sum[r]-sum[l-1];
}
int query(int n,int m,int k)
{
int ans=0;
for(int l=1,r=0;l<=MIN(n,m);l=r+1)
{
r=MIN(m/(m/l),n/(n/l));
ans+=ask(l,r)*(n/l/k)*(m/l/k);
}
return ans;
}
int n,a,b,c,d,k;
void solve()
{
pre(50000);
n=read();
F(i,1,n)
{
a=read()-1,b=read(),c=read()-1,d=read(),k=read();
int ans=query(b,d,k);
ans-=query(a,d,k)+query(b,c,k)-query(a,c,k);
put(ans);
}
}
int main()
{
int T=1;
// T=read();
while(T--) solve();
return 0;
}
「SDOI2015」约数个数和
前置:
原式:
改为枚举因子 \(x,y\):
将 \([\gcd(x,y)=1]\) 根据套路化去:
枚举 \(d\):
后面两个循环改为枚举 \(d\) 的倍数:
考虑预处理 \(f(x)=\sum\limits_{i=1}^x \lfloor\frac{x}{i}\rfloor\)。答案为 \(\sum\limits_{d=1}^{\min(n,m)}\mu (d)f(\lfloor\frac{n}{d}\rfloor)f(\lfloor\frac{m}{d}\rfloor)\)。
数列分块即可。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=5e4+10;
sd vector<int> pr;
int p[N],mu[N],sum[N],f[N];
void pre(int n)
{
mu[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(i*it>n) break;
p[i*it]=1;
if(i%it==0)
{
mu[i*it]=0;
break;
}
mu[i*it]=-mu[i];
}
}
F(i,1,n) sum[i]=sum[i-1]+mu[i];
F(x,1,n)
{
for(int l=1,r=0;l<=x;l=r+1)
{
r=x/(x/l);
f[x]+=(r-l+1)*(x/l);
}
}
}
void solve()
{
int n=read(),m=read();
if(n>m) sd swap(n,m);//默认n<=m
int ans=0;
for(int l=1,r=0;l<=n;l=r+1)
{
r=MIN(n/(n/l),m/(m/l));
ans+=(sum[r]-sum[l-1])*f[n/l]*f[m/l];
}
put(ans);
}
signed main()
{
pre(50000);
int T=1;
T=read();
while(T--) solve();
return 0;
}
「BZOJ2693」jzptab
弱化版:P1829。原题有多测。
枚举 \(\lfloor\frac{n}{i}\rfloor\) 和 \(\lfloor\frac{m}{i}\rfloor\):
按套路化简
最外层循环 \(d\) 不变,枚举 \(p\):
(\(i,j\) 枚举的是 \(p\) 的 \(i\) 倍和 \(j\) 倍)
设 \(f(x)=\sum\limits_{i=1}^x,k=dp\):
考虑枚举 \(k\):
枚举 \(d\) 改为枚举 \(p\)(\(\frac{k}{d}\)):
考虑 \(F(k)=\sum\limits_{p\mid k}\mu(p)p\),容易看出 \(F\) 是积性函数,可以线性筛筛出,最后数列分块即可。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e7+10,P=100000009;
sd vector<int> pr;
int p[N],g[N],sum[N];
void pre(int n)
{
g[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),g[i]=(P+1-i)%P;
Fr(pr)
{
if(i*it>n) break;
p[i*it]=1;
if(i%it==0)
{
g[i*it]=g[i];
break;
}
g[i*it]=g[i]*g[it]%P;
}
}
F(i,1,n) sum[i]=(sum[i-1]+g[i]*i%P)%P;
}
int f(int k)
{
return k*(k+1)/2;
}
int n,m;
void solve()
{
n=read(),m=read();
if(n>m) sd swap(n,m);
int ans=0;
for(int l=1,r=0;l<=n;l=r+1)
{
r=MIN(n/(n/l),m/(m/l));
int k1=f(n/l)%P,k2=f(m/l)%P;
(ans+=(sum[r]-sum[l-1]+P)%P*k1%P*k2%P)%=P;
}
put(ans);
}
signed main()
{
pre(10000000);
int T=1;
T=read();
while(T--) solve();
return 0;
}
「SDOI2017」数字表格
即求 \(\prod\limits_{i=1}^n\prod\limits_{j=1}^mf_{\gcd(i,j)}\),\(f\) 是斐波那契数列。
化简式子。
考虑对于每个 \(k\),求出 \(f_k\) 会乘多少次:
右上角指数是套路:
带回原式:
然后令 \(T=kd\),枚举 \(T\):
设 \(F(T)=\sum\limits_{k\mid T} f_{k}^{\mu(\frac{T}{k})}\),枚举倍数可以 \(O(n\log n)\) 求解 \(F\)。
原式即 \(\prod\limits_{T=1}^nF(T)^{\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor}\),维护 \(F\) 前缀积,数列分块即可。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e6+10,P=1e9+7;
sd vector<int> pr;
int p[N],mu[N],f[N],g[N],FF[N];//g代表FF前缀积
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%P;
a=a*a%P;
b>>=1;
}
return res;
}
int inv(int x)
{
return ksm(x,P-2);
}
void pre(int n)
{
mu[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(it*i>n) break;
p[i*it]=1;
if(i%it==0) break;
mu[i*it]=-mu[i];
}
}
f[1]=f[2]=1;
F(i,3,n) f[i]=(f[i-1]+f[i-2])%P;
F(i,1,n) FF[i]=1;
F(i,1,n)
{
for(int j=1;i*j<=n;j++)
{
if(mu[j]>0) FF[i*j]=FF[i*j]*ksm(f[i],mu[j])%P;
else FF[i*j]=FF[i*j]*inv(ksm(f[i],-mu[j]))%P;
}
}
g[0]=1;
F(i,1,n) g[i]=g[i-1]*FF[i]%P;
}
int n,m;
void solve()
{
n=read(),m=read();
if(n>m) sd swap(n,m);
int ans=1;
for(int l=1,r=0;l<=n;l=r+1)
{
r=MIN(n/(n/l),m/(m/l));
(ans*=ksm((g[r]*inv(g[l-1]))%P,(n/l)*(m/l)))%=P;
}
put(ans);
}
signed main()
{
pre(1000000);
int T=1;
T=read();
while(T--) solve();
return 0;
}
「BZOJ2820」YY的GCD
即 \(\sum\limits_{p\in \mathbb{Prime}}\sum\limits_{i=1}^n\sum\limits_{j=1}^m[\gcd(i,j)=p]\)。
还是先考虑化简。
考虑令 \(T=pd\),枚举 \(T\):
考虑预处理 \(F(T)=\sum_{p\mid T,p\in \mathbb{Prime}}\mu(\frac{T}{p})\) 以及它的前缀和,对每个质数枚举倍数做到 \(O(m\log m)\) 是容易的,\(m\) 为质数数量。
然后数列分块即可。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e7+10;
sd vector<int> pr;
int p[N],mu[N],f[N];
void pre(int n)
{
mu[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(i*it>n) break;
p[i*it]=1;
if(i%it==0) break;
mu[i*it]=-mu[i];
}
}
Fr(pr)
{
F(i,1,n/it)
{
if(i*it>n) break;
f[i*it]+=mu[i];
}
}
F(i,1,n) f[i]+=f[i-1];
}
int n,m;
void solve()
{
n=read(),m=read();
if(n>m) sd swap(n,m);
int ans=0;
for(int l=1,r=0;l<=n;l=r+1)
{
r=MIN(n/(n/l),m/(m/l));
ans+=(f[r]-f[l-1])*(n/l)*(m/l);
}
put(ans);
}
signed main()
{
pre(10000000);
int T=1;
T=read();
while(T--) solve();
return 0;
}
剩下的题都差不多,就不写过程了,留作练习。
「BZOJ4407」于神之怒加强版
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=5e6+10,P=1e9+7;
int n,m,mu[N],idk[N];
sd vector<int> pr;
int p[N];
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%P;
a=a*a%P;
b>>=1;
}
return res;
}
int f[N];
void pre(int n,int K)
{
f[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),f[i]=(ksm(i,K)-1+P)%P;
Fr(pr)
{
if(i*it>n) break;
p[i*it]=1;
if(i%it==0)
{
f[i*it]=f[i]*ksm(it,K)%P;
break;
}
f[i*it]=f[i]*f[it]%P;
}
}
F(i,1,n) (f[i]+=f[i-1])%=P;
}
void solve()
{
n=read(),m=read();
if(n>m) sd swap(n,m);
int ans=0;
for(int l=1,r=0;l<=n;l=r+1)
{
r=MIN(n/(n/l),m/(m/l));
(ans+=(f[r]-f[l-1]+P)*(n/l)%P*(m/l)%P)%=P;
}
put(ans);
}
signed main()
{
int T=read(),K=read();
pre(5000000,K);
while(T--) solve();
return 0;
}
GCD SUM
题目名字是我校OJ的,洛谷原题好像叫疯狂的gcd。
就是求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^n\gcd(i,n)\)。
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e5+10;
sd vector<int> pr;
int p[N],mu[N];
void pre(int n)
{
mu[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(it*i>n) break;
p[it*i]=1;
if(i%it==0) break;
mu[it*i]=-mu[i];
}
}
}
int n;
void solve()
{
n=read();
int ans=0;
F(k,1,n) F(d,1,n/k)
{
ans+=k*mu[d]*(n/k/d)*(n/k/d);
}
put(ans);
}
signed main()
{
pre(100000);
int T=1;
// T=read();
while(T--) solve();
return 0;
}
「NOI 2010」能量采集
#include<bits/stdc++.h>
#define sd std::
#define int long long
#define F(i,a,b) for(int i=(a);i<=(b);i++)
#define ff(i,a,b) for(int i=(a);i>=(b);i--)
#define MIN(x,y) (x<y?x:y)
#define MAX(x,y) (x>y?x:y)
#define me(x,y) memset(x,y,sizeof x)
#define pii sd pair<int,int>
#define X first
#define Y second
#define Fr(a) for(auto it:a)
int read(){int w=1,c=0;char ch=getchar();for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;for(;ch>='0'&&ch<='9';ch=getchar()) c=(c<<3)+(c<<1)+ch-48;return w*c;}
void printt(int x){if(x>9) printt(x/10);putchar(x%10+48);}
void print(int x){if(x<0) putchar('-'),printt(-x);else printt(x);}
void put(int x){print(x);putchar('\n');}
void printk(int x){print(x);putchar(' ');}
const int N=1e5+10;
sd vector<int> pr;
int p[N],mu[N];
void pre(int n)
{
mu[1]=1;
F(i,2,n)
{
if(!p[i]) pr.emplace_back(i),mu[i]=-1;
Fr(pr)
{
if(it*i>n) break;
p[it*i]=1;
if(i%it==0) break;
mu[it*i]=-mu[i];
}
}
}
int n,m;
void solve()
{
n=read(),m=read();
if(n>m) sd swap(n,m);
int ans=0;
F(k,1,n) F(d,1,n/k)
{
ans+=k*mu[d]*(n/k/d)*(m/k/d);
}
put(2*ans-n*m);
}
signed main()
{
pre(100000);
int T=1;
// T=read();
while(T--) solve();
return 0;
}
https://www.luogu.com/article/998kttnc
https://www.cnblogs.com/linzhengmin/p/11060871.html
https://www.cnblogs.com/linzhengmin/p/10994520.html

浙公网安备 33010602011771号