数论习题(一)
(一).推式子题
- P2568 GCD
给定
其中
推式子:
线性筛
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
using namespace std;
typedef long long ll;
const int MAXN=1e7+10;
ll n,prime[MAXN],phi[MAXN],v[MAXN],tot,sumphi[MAXN];
ll ans;
void shai(){//欧拉筛求质数&欧拉函数
phi[1]=1;
for(int i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > n/i) break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) phi[i*prime[j]] =phi[i] * prime[j];
else phi[i*prime[j]] =phi[i] *(prime[j]-1);
}
}
for(int i=1;i<=n;i++) phi[i]+=phi[i-1];
}
int main(){
scanf("%lld",&n);
shai();
for(int i=1;i<=tot;i++) ans+=2*phi[n/prime[i]]-1;
printf("%lld\n",ans);
return 0;
}
- P2398 GCD SUM
给定
枚举
筛
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll n;
ll prime[N],v[N],phi[N],tot;
void shai(){//欧拉筛求质数&欧拉函数
phi[1]=1;
for(int i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > n/i) break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) phi[i*prime[j]] =phi[i] * prime[j];
else phi[i*prime[j]] =phi[i] *(prime[j]-1);
}
}
for(int i=1;i<=n;i++) phi[i]+=phi[i-1];
}
int main(){
scanf("%lld",&n);ll ans=0;
shai();
for(int d=1;d<=n;d++){
ans+=d*(2*phi[n/d] -1);
}printf("%lld\n",ans);
return 0;
}
- P2522 [HAOI2011] Problem b
给定
先通过容斥拆成四部分:记
记
开始推
最后这个式子可以数论分块求,这样这道题就做完了。
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=5e4+10;
ll T=1;
ll a,b,c,d,k;
ll prime[N],v[N],mu[N],tot,summu[N];
void shai(){
mu[1]=1;
for(int i=2;i<=N-10;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > (N-10)/i) break;
v[i*prime[j]]=prime[j];
if(i % prime[j]==0) mu[i*prime[j]]=0;
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=N-10;i++) summu[i]=summu[i-1]+mu[i];
}
ll calc(ll n,ll m){
ll res=0,l=1,lim=min(n,m);bool Out=0;
while(l<=lim){
ll pn=n/l,pm=m/l;
ll r=min(n/pn,m/pm);
if(r>=lim) r=lim,Out=1;
res+=(summu[r]-summu[l-1]) * pn * pm;
if(Out) break;
l=r+1;
}
return res;
}
ll calc2(ll n,ll m){
ll res=0;
for(int d=1;d<=min(n,m);d++){
res+=mu[d] * (n/d) * (m/d);
}return res;
}
int main(){
shai();
scanf("%lld",&T);
for(int Case=1;Case<=T;Case++){
scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&k);
ll ans=0;
ll A=ceil((double)a/k)-1,B=b/k,C=ceil((double)c/k)-1,D=d/k;
ans=calc(B,D)-calc(B,C)-calc(A,D)+calc(A,C);
printf("%lld\n",ans);
}
return 0;
}
- P1829 [国家集训队] Crash的数字表格 / JZPTAB
给定
由小学奥数可知:
接下来我们化简括号内,
此式中
所以:
而中间那个求和符号:
所以:
而原式显然也可以数论分块来做,两个合二为一就能过掉此题了。
最后放一下原式最终的样子:
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=1e7+10,mod=20101009,inv2=10050505,inv4=15075757;
ll n,m;
ll prime[N],v[N],mu[N],tot;
ll sum2[N];
void shai(ll n){
mu[1]=1;
for(ll i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
mu[i]=-1;
}
for(ll j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > n/i) break;
v[i*prime[j]]=prime[j];
if(i % prime[j]==0) mu[i*prime[j]]=0;
else mu[i*prime[j]]=-mu[i];
}
}
}
ll calc(ll n,ll m){
ll res=0;
ll l=1,lim=min(n,m);bool Out=0;
while(l<=lim){
ll nl=n/l,ml=m/l,r=min(n/nl,m/ml);
if(r>=lim) r=lim,Out=1;
ll cnt=nl*(nl+1)%mod*ml%mod*(ml+1)%mod *inv4%mod;
res=(res+(sum2[r]-sum2[l-1])*cnt%mod)%mod;
if(Out) break;
l=r+1;
}
return (res%mod+mod)%mod;
}
int main(){
scanf("%lld%lld",&n,&m);
shai(max(n,m));
for(ll i=1;i<=max(n,m);i++) sum2[i]=(sum2[i-1]+i*i%mod*mu[i]%mod)%mod;
ll ans=0,l=1,lim=min(n,m);
bool Out=0;
while(l<=lim){
ll r=min(n/(n/l),m/(m/l));
if(r>=lim) r=lim,Out=1;
ans=(ans+calc(n/l,m/l)*(r*(r+1)%mod-l*(l-1)%mod)%mod*inv2%mod)%mod;
if(Out) break;
l=r+1;
}
printf("%lld\n",(ans%mod+mod)%mod);
return 0;
}
- BZOJ2694. Lcm
给定
直接推式子,把
对
这样子上一题中的
发现
令
则
考虑用线性筛筛出来
这样就可以用线性筛筛出
最后对原式进行数论分块,卡卡常就过去了。
点击查看代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef int ll;
const int N=4e6+10,mod=1<<30;
ll T=1;
ll n,m,sum[N];
ll prime[N],v[N],g[N],h[N],tot;
void shai(ll n){
h[1]=1;
for(int i=2;i<=n;i++){
if(v[i]==0){
prime[++tot]=i;
v[i]=i;g[i]=i;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) g[i*prime[j]]=g[i]*prime[j];
else g[i*prime[j]]=prime[j];
}
}
for(int i=2;i<=n;i++){
if(i==g[i]){
if(g[i]==v[i]) h[i]=1-v[i];
else if(g[i]==v[i]*v[i]) h[i]=-v[i];
else h[i]=0;
}else h[i]=(h[i/g[i]]*h[g[i]])%mod;
}
for(int i=1;i<=n;i++) h[i]=(h[i]*i%mod+h[i-1])%mod,sum[i]=(sum[i-1]+i)%mod;
}
int main(){
shai(N-10);
scanf("%d",&T);
for(int Case=1;Case<=T;Case++){
scanf("%d%d",&n,&m);
ll ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
ll nl=n/l,ml=m/l;
r=min(n/nl,m/ml);
ans=(ans+sum[nl]*sum[ml] %mod *(h[r]-h[l-1])%mod)%mod;
}
printf("%d\n",(ans+mod)%mod);
}
return 0;
}
- P4449 于神之怒加强版
给定
套路!!枚举
莫反拆开:
枚举
也就是:
枚举
后面那个东西其实是
筛出来后求个前缀和,然后数论分块即可。
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=5e6+10,mod=1e9+7;
ll T=1,k;
ll n,m;
ll prime[N],tot,v[N],mu[N];
ll g[N],f[N];
ll qpow(ll a,ll n){
ll res=1;
while(n){
if(n&1) res=(res*a) %mod;
a=(a*a)%mod,n>>=1;
}return res;
}
void shai(ll n){
f[1]=mu[1]=g[1]=f[1]=1;
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i,prime[++tot]=i,mu[i]=-1;
g[i]=i;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) g[i*prime[j]]=g[i]*prime[j],mu[i*prime[j]]=0;
else g[i*prime[j]]=prime[j],mu[i*prime[j]]=-mu[i];
}
}
for(int i=2;i<=n;i++){
if(i==g[i]){
ll tot=0;
while(g[i]&&g[i]%v[i]==0) tot++,g[i]/=v[i];
f[i]=qpow(v[i],k*tot)-qpow(v[i],k*(tot-1));
f[i]=(f[i]%mod+mod)%mod;
}else f[i]=(f[i/g[i]]*f[g[i]])%mod;
}
for(int i=1;i<=n;i++) f[i]+=f[i-1];
}
int main(){
scanf("%lld%lld",&T,&k);
shai(N-10);
for(int Case=1;Case<=T;Case++){
scanf("%lld%lld",&n,&m);
ll ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
ll nl=n/l,ml=m/l;r=min(n/nl,m/ml);
(ans+=nl*ml%mod*(f[r]-f[l-1])%mod)%=mod;
}printf("%lld\n",(ans+mod)%mod);
}
return 0;
}
- P3327 [SDOI2015] 约数个数和
给定
其中
蒙结论,蒙出来要求的就是这个东西:
原理大概就是:枚举一对互质(为什么不会遗漏我也不知道)的数对
但是为什么枚举互质的
接下来这个东西就能做了,用莫反变一下
套路性地,把
也就是:
我们
附:关于此题正常一些的想法
正常情况下应该蒙出这个结论的:
我们先把这个证了。
记:
我们知道
对于某个质因子
对于等式左边,显然取法有
对于等式右边,有三类取法:
加起来一共有
而每个质因子都是一样的情形,所以总数相同。
有了这个结论,我们正常地推一遍式子:
直接考虑更换
而
此时已经和我蒙出来的结论一模一样了,这就OK了。
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=5e4+10;
ll TT=1;
ll n,m;
ll prime[N],v[N],mu[N],tot;
ll ji[N];
ll calc(ll n){
if(ji[n]) return ji[n];
ll res=0;
for(int l=1,r;l<=n;l=r+1){
ll nl=n/l;
r=n/nl;
res+=nl * (r-l+1);
}return ji[n]=res;
}
void shai(ll n){
mu[1]=1;
for(ll i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
mu[i]=-1;
}
for(ll j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > n/i) break;
v[i*prime[j]]=prime[j];
if(i % prime[j]==0) mu[i*prime[j]]=0;
else mu[i*prime[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) mu[i]+=mu[i-1],ji[i]=calc(i);
}
int main(){
shai(N-10);
scanf("%lld",&TT);
for(int Case=1;Case<=TT;Case++){
scanf("%lld%lld",&n,&m);
ll ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
ll nl=n/l,ml=m/l;
r=min(n/nl,m/ml);
ans+=(mu[r]-mu[l-1]) * ji[nl]*ji[ml];
}
printf("%lld\n",ans);
}
return 0;
}
- BZOJ3561. DZY Loves Math VI
给定
直接推式子,枚举
套路地,枚举
本来我觉得的这个东西还能再变形,再做,于是想破脑袋想了一个上午没想出来。
但其实,我们分析分析复杂度:
如果再算上
因为当
预处理之后,可以同时把幂的前缀和算出来,从时间复杂度就到了
所以直接枚举算这个式子就好了……我为什么浪费了一个上午啊啊啊啊啊啊啊啊啊啊啊啊
点击查看代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=5e5+10,mod=1000000007;
int n,m;
int prime[N],v[N],mu[N],tot;
ll now[N],sum[N];
int tmp[N];
ll qpow(ll a,int n){
int res=1,stn=n,sta=a;
while(n){
if(n&1) res=(1ll*res*a)%mod;
a=(a*a)%mod,n>>=1;
}
return res;
}
void shai(int n){
mu[1]=1,v[1]=1;
for(int i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i] || prime[j] > n/i) break;
v[i*prime[j]]=prime[j];
if(i % prime[j]==0) mu[i*prime[j]]=0;
else mu[i*prime[j]]=-mu[i];
}
}
}
int main(){
shai(N-10);
scanf("%d%d",&n,&m);
int ans=0;
for(int d=1;d<=min(n,m);d++){
int tot=0,lim=min(n/d,m/d);
for(int i=1;i<=max(n/d,m/d);i++){
now[i]=((now[i]!=0)?now[i] *i%mod:qpow(i,d))%mod;
sum[i]=(sum[i-1]+now[i])%mod;
}
for(int k=1;k<=lim;k++){
int cnt1=0,cnt2=0;tmp[1]=1;
int lim1=n/(d*k),lim2=m/(d*k);
tot=(tot+ mu[k] * qpow(k,2*d)%mod *sum[lim1]%mod *sum[lim2]%mod )%mod;
}
ans=(ans+tot*qpow(d,d)%mod)%mod;
}printf("%d\n",(ans+mod)%mod);
return 0;
}
- P5518 [MtOI2019] 幽灵乐团 / 莫比乌斯反演基础练习题
推式子大成!
求:
其中:
预处理
我们将分子分母分离:
也就是:
对分子进行子母分离,分母扔下面:
关键东西
我们定义一个神奇函数
这个神奇函数可以
有了神奇函数,我们就可以推导一下这个式子(待会要用到两次,所以扔前头了):
枚举
莫反拆开往外提:
把上面的求和捋下来,有:
枚举
我们把底数上的分子
分子
因为底数为
这东西熟悉的不得了,不就是
这玩意一眼顶针,铁定为
分母
我们记
没办法拆开了,于是我们考虑一下这玩意的实际含义。
我们对
我们也对
注意到当存在
我们对
此时
此时
我们把
我们我们发现,当指数
于是我们考虑某一个质因数
既然
所以分子上的个数就是
同理,分母上出现的个数是
根据一点点组合数学的知识,我们会发现无论
既然对于每个质因子,上下的出现次数都相等,那么这一坨玩意就是
即
综上所述,我们发现分子绝对是
所以原式就变成了:
预处理
将
1.分子
把
发现内层循环就是
观察左边的式子,由于是求乘积,可以把
很优美了,预处理阶乘后快速幂就行。
2.分母
发现两项非常相似,于是我们令:
此时分母就成了:
而
虽然我有统统带回去的毛病,但懒了,不把原式子放出来了!
这样这部分就做完了!
将
分子
把关于
发现在
求和公式套进去:
接下来,我们先处理内部:
把幂放进去:
把
两部分结构又相同了,我们考虑其中一个:
可以发现,在
指数就成了求和公式,这玩意我们熟悉的不得了,于是:
然后一层层往回带,分子部分就变成了:
预处理
分母
发现两项非常相似,于是我们令:
此时分母就成了:
接下来处理
接下来套路处理内部就行了,枚举
莫反拆开,枚举
把
预处理一下
继续推,把指数上的求和放下来,有:
枚举
也就是:
我们找到老熟人了,里面的东西其实就是
于是我们预处理这个东西的前缀积和其逆元,就有:
一次数论分块即可。
把
我们处理分子分母吧……好累啊……
分子
我们记:
则分子就是
我们推
大头敲开方向要对鲜美鸡汤必须喝掉
枚举
蛮好的,有
一次数论分块即可。
分母
我们令:
那么分母就是:
推
枚举
拆成
发现
然后我们枚举
得,这一步我退出来乐死了。
分成两部分,就是:
后者,又出现了我们文章最开头推的式子,于是就成了:
两层数论分块,终于,搞!定!
这b题终于让我写!!完!!了!!
点击查看代码
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
#define fr(a) freopen(a,"r",stdin)
#define fw(a) freopen(a,"w",stdout)
#define MP(a,b) make_pair(a,b)
#define DEBUG
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll T=1,mod,pmod;
ll A,B,C,ans1,ans2,ans3;
ll prime[N],v[N],tot,mu[N],smu[N],smu2[N],phi[N];
ll jie[N],invj[N],tphi[N],invtphi[N],sphi[N];
ll slf[N],invslf[N];
ll gm[N],invgm[N],invsmu[N];
ll nai[N],sn[N],invsn[N],sn2[N],invsn2[N];
ll qpow(ll a,ll n){
n=(n%pmod +pmod)%pmod;
a %=mod;
ll res=1;
while(n){
if(n&1) res=(res*a)%mod;
a=(a*a)%mod;n>>=1;
}return res;
}
void shai(ll n){
mu[1]=phi[1]=1;
for(ll i=2;i<=n;i++){
if(v[i]==0){
v[i]=i;prime[++tot]=i;
mu[i]=-1;phi[i]=i-1;
}for(ll j=1;j<=tot;j++){
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
if(i%prime[j]==0) mu[i*prime[j]]=0,phi[i*prime[j]]=phi[i]*(prime[j]);
else mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
void init(ll n){
jie[0]=invj[0]=slf[0]=invslf[0]=1;
gm[0]=invgm[0]=1;
tphi[0]=invtphi[0]=1;
pmod=mod-1;
for(ll i=1;i<=n;i++){
jie[i]=(jie[i-1]*i)%mod;
invj[i]=qpow(jie[i],mod-2);
slf[i]=slf[i-1] * qpow(i,i)%mod;
invslf[i]=qpow(slf[i],mod-2);
smu[i]=(smu[i-1]+mu[i])%pmod;
smu2[i]=(smu2[i-1] + mu[i] * i * i%pmod)%pmod;
gm[i]=gm[i-1] * qpow(i,mu[i])%mod;
invgm[i]=qpow(gm[i],-1);
tphi[i]=tphi[i-1] * qpow(i,phi[i]) %mod;
invtphi[i]=qpow(tphi[i],-1);
sphi[i] = (sphi[i-1] + phi[i]) %pmod;
nai[i]=1;
}
for(int i=1;i<=tot;i++){
ll now=prime[i];
if(now>n) break;
while(now<n){
nai[now]=prime[i];
now *= prime[i];
}
}
sn[0]=invsn[0]=sn2[0]=invsn2[0]=1;
for(int i=1;i<=n;i++){
sn[i]=(sn[i-1] * nai[i])%mod;
invsn[i]=qpow(sn[i],-1);
sn2[i]= sn2[i-1] * qpow(nai[i],1ll * i*i%pmod) %mod;
invsn2[i]=qpow(sn2[i],-1);
}
}
ll f32(ll a,ll b){
ll res=1,lim=min(a,b);
for(ll l=1,r;l<=lim;l=r+1){
ll ad=a/l,bd=b/l;r=min(a/ad,b/bd);
(res *= qpow(sn[r] * invsn[l-1] %mod ,ad * bd%pmod) )%=mod;
}return res;
}
ll f0(ll a,ll b,ll c){
return qpow(f32(a,b),c);
}
void work1(){
ll ans=qpow(qpow(jie[A],B)*qpow(jie[B],A)%mod,C),tot=1;
(ans*=qpow(f0(A,B,C) * f0(A,C,B) %mod,mod-2)) %=mod;
ans1=(ans+mod)%mod;
}
ll f1(ll a,ll b,ll c){
ll res=1,lim=min(a,b);
for(ll l=1,r;l<=lim;l=r+1){
ll ad=a/l,bd=b/l;r=min(a/ad,b/bd);
ll zhi=(ad*(ad+1)/2) %pmod *( (bd*(bd+1)/2) %pmod) %pmod;
(res *= qpow(sn2[r] * invsn2[l-1] %mod ,zhi) )%=mod;
}
res= qpow(res,c*(c+1)/2);
return res;
}
void work2(){
ll zi=1,ans=qpow(f1(A,B,C) * f1(A,C,B)%mod,mod-2);
zi=qpow(slf[A],B*(B+1)/2) * qpow(slf[B],A*(A+1)/2) %mod;
(ans *=qpow(zi,C*(C+1)/2)) %=mod;
ans2=(ans+mod)%mod;
}
ll f2(ll a,ll b,ll c){
ll res=1,limd=min(min(a,b),c);
for(ll ld=1,rd;ld<=limd;ld=rd+1){
ll ad=a/ld,bd=b/ld,cd=c/ld;
rd=min(min(a/ad,b/bd),c/cd);
(res *= qpow(gm[rd] * invgm[ld-1]%mod,ad*bd%pmod*cd%pmod) )%=mod;
ll tmp=qpow(jie[ad],bd*cd%pmod);
(res *= qpow(tmp,(smu[rd]-smu[ld-1])%pmod))%=mod;
}return res;
}
ll f2_5(ll a,ll b,ll c){
ll res=1,lim=min(min(a,b),c);
for(ll l=1,r;l<=lim;l=r+1){
ll at=a/l,bt=b/l,ct=c/l;
r=min(min(a/at,b/bt),c/ct);
(res *= qpow(tphi[r] * invtphi[l-1]%mod , at * bt %pmod *ct %pmod))%=mod;
ll tmp=qpow(jie[at],bt*ct%pmod);
(res *= qpow(tmp,(sphi[r] - sphi[l-1]) %pmod))%=mod;
}return res;
}
ll f3(ll a,ll b,ll c){
ll limt=min(min(a,b),c),res=1;
for(int lt=1,rt;lt<=limt;lt=rt+1){
ll at=a/lt,bt=b/lt,ct=c/lt;
rt=min(min(a/at,b/bt),c/ct);
ll tmp=tphi[rt] * invtphi[lt-1] %mod;
(res *= qpow(tmp,at * bt%pmod * ct%pmod) )%=mod;
tmp=qpow(f32(at,bt) %mod,ct) %mod;
(res *= qpow(tmp,(sphi[rt] - sphi[lt-1]) %pmod) )%=mod;
}return res;
}
void work3(){
ll zi=f2_5(A,B,C) * f2_5(B,A,C) %mod;
ll mu=f3(A,B,C) * f3(A,C,B)%mod;
ans3=(zi*qpow(mu,-1)%mod+mod)%mod;
return;
}
int main(){
scanf("%lld%lld",&T,&mod);
shai(N-10);
init(N-10);
for(int Case=1;Case<=T;Case++){
scanf("%lld%lld%lld",&A,&B,&C);
work1();work2();
work3();
printf("%lld %lld %lld\n",ans1,ans2,ans3);
}
return 0;
}
- P1587 [NOI2016] 循环之美
给定
我们有一个不会证的结论:当分母和进制互质,分子和分母互质时,这个分数是
于是就成了求以下东西:
推式子,对后一项莫反拆开:
显然,我们有
故我们发现后面是一个规模缩小的原问题。
我们设:
于是我们有递推式:
直接开搜就行了。
终止状态:
-
当
或 时, 。 -
当
时, ,基本推式子没的说。
所以我们用杜教筛处理
-
我们提前预处理
的“所有因数”的所有因数,显然空间复杂度是 的,可以接受,此时枚举 的复杂度直接从 降到了 ,很大的优化。 -
当
时,不往下搜,显然,这个优化更大。
加入这两个剪枝后就能顺利过此题了。
特别鸣鸣鸣鸣鸣鸣鸣鸣谢 Qcfff,基本上思路全都是抄他的呜呜呜。
P3704 [SDOI2017] 数字表格
给定
这不乱做?
枚举
里面的东西预处理,
然后做完了!整除分块。水紫真舒服
P2257 YY的GCD
求:
莫个反先:
枚举
后面的可以预处理。求个前缀和。做完了。
P1891 疯狂 LCM
挺好玩。求:
先拆一下:
枚举
发现好玩的事:
我们先处理
枚举
也就是:
然后
也可以
于是我们
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构