回头看看自己真菜。 ---题记
正题
1.类欧几里得算法
2.中国剩余定理
3.简单的数论方法
例题
1.一个人的数论:
给出n的质因数分解:,求
。
这条式子卷上mu。
然后我们发现后面那个sigma的值只跟有关,因为他是一个m+1次函数,这个可以很容易就想到,因为
那么他就可以用m+2个点来确定,直接拉格朗日插值法求出这个函数每一项的系数。
假设第i位的系数位,那么原来的式子就等于。
那么现在我们再来考虑后面这个式子,我们设明显是一个积性函数,
也明显是一个积性函数。
那么后面那个sigma相当于求,*为狄利克雷卷积。
两个积性函数的狄利克雷卷积是个积性函数,所以我们只需要求出这个东西分别在下的值就好了。
对于每一个,我们考虑g不为0的位置只有
所以算这个东西就是
的。bzoj3601有兴趣的同学可以做一做。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int d,w;
int p[1010];
long long ty[1010],inv[1010];
long long mod=1e9+7;
long long f[1010],ans[1010];
long long now[1010],num[1010];
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
void build(){
long long tot=1,tt=0;f[0]=1;
for(int i=1;i<=d+2;i++){
for(int k=i;k>=1;k--) f[k]=(f[k-1]+f[k]*(mod-i)%mod)%mod;
(f[0]*=mod-i)%=mod;
(tot*=mod-i)%=mod;
}
for(int i=1;i<=d+2;i++){
for(int j=d+2;j>=0;j--) now[j]=f[j];
for(int j=d+2;j>=1;j--)
(now[j-1]+=now[j]*i%mod)%=mod;
tot=1;
for(int j=1;j<i;j++) (tot*=i-j)%=mod;
for(int j=i+1;j<=d+2;j++) (tot*=i+mod-j)%=mod;
(tt+=ksm(i,d))%=mod;
tot=ksm(tot,mod-2);(tot*=tt)%=mod;
for(int j=0;j<=d+1;j++)
(ans[j]+=now[j+1]*tot%mod)%=mod;
}
}
long long get_ans(int x){
long long ans=1;
for(int i=1;i<=w;i++) (ans*=(ksm(num[i],x)+mod-ty[i]*ksm(inv[i],x)%mod)%mod)%=mod;
return ans;
}
int main(){
scanf("%d %d",&d,&w);
int x;for(int i=1;i<=w;i++) scanf("%d %d",&p[i],&x),ty[i]=ksm(p[i],d),num[i]=ksm(p[i],x);
for(int i=1;i<=w;i++) inv[i]=ksm(p[i],mod-2)*num[i]%mod;
build();long long op=0;
for(int i=0;i<=d+1;i++) (op+=ans[i]*get_ans(i)%mod)%=mod;
printf("%lld",op);
}
2.怎样跑得更快
给定,此题所有式子对998244353取膜,现有序列b满足:
,求序列z。
首先我们可以把题目转化一下,变成
我们令,那么原来的式子就变成了。
现在就是知道了x,求y。
莫比乌斯反演一下:
我们令,那么前两个考虑当T一定的时候,g,d必定是T的约数且相乘等于T,那么就相当于求狄利克雷卷积的第T项。
我们把后面那坨东西叫做
那么我们现在就知道了
这个像啥,莫比乌斯反演
那么我们就知道f了。
发现前面那个东西是可以线性筛+狄利克雷卷积(或者不用也行),搞出来的。
我们把它放到左边
相当于什么?
!!!
这不是另外一个莫比乌斯反演式子吗?
做完了?退回zj是很简单的吧?
这题因为xi没开long long被卡到自闭。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=100000;
int n,c,d,q,w;
long long mu[maxn+10],sum[maxn+10],t[maxn+10],inv[maxn+10],x[maxn+10];
int p[maxn+10];
const long long mod=998244353;
bool vis[maxn+10];
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
long long get_q(long long x,long long t){
return t>=0?ksm(x,t):ksm(ksm(x,mod-2),-t);
}
void get_sum(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n/i;j++)
(sum[i*j]+=mu[i]*t[j]%mod)%=mod;
}
void get_mu(){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) {p[++p[0]]=i;mu[i]=mod-1;}
for(int j=1;j<=p[0] && i*p[j]<=n;j++){
vis[i*p[j]]=true;
if(i%p[j]==0) break;
if(mu[i]!=0) mu[i*p[j]]=mod-mu[i];
}
}
}
void solve(){
for(int i=1;i<=n;i++) scanf("%lld",&x[i]),(x[i]*=inv[i])%=mod;
for(int i=1;i<=n;i++)
for(int j=2;j<=n/i;j++)
(x[i*j]+=mod-x[i])%=mod;
for(int i=1;i<=n;i++){
if(x[i]==0) continue;
if(sum[i]==0){
printf("-1");return ;
}
(x[i]*=sum[i])%=mod;
}
for(int i=1;i<=n;i++)
for(int j=2;j<=n/i;j++)
(x[i]+=mu[j]*x[i*j]%mod)%=mod;
for(int i=1;i<=n;i++)
printf("%lld ",x[i]*inv[i]%mod);
}
int main(){
scanf("%d %d %d %d",&n,&c,&d,&q);w=c-d;
for(int i=1;i<=n;i++) t[i]=get_q(i,w);
for(int i=1;i<=n;i++) inv[i]=get_q(i,-d);
get_mu();get_sum();
for(int i=1;i<=n;i++) sum[i]=get_q(sum[i],mod-2);
while(q--) solve(),printf("\n");
}
3.给出,求
很明显要转化成gcd然后莫比乌斯反演。
前面可以用
的时间来求出
的前缀和。(其中有一些值是相等的,一共有
个不同的值。
然后每一次用根号时间求一下前面那个式子的前缀和,时间复杂度就是积分一下???即是正确的复杂度。
网上没有题,所以自己写了一个对拍,数据包和标程。小数据都是对的,如果有哪位神仙发现这份代码有问题,请咨询蒟蒻。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int bound=(long long)4641588;
const long long mod=998244353;
long long n,sum[bound+1],key[bound+1];
long long op[bound+1];
bool tf[bound+1];
int p[bound+1];
bool vis[bound+1];
long long inv2,inv6;
long long get_num(long long l,long long r){
long long data=r-l+1;
l%=mod;r%=mod;data%=mod;
return (l+r)*data%mod*inv2%mod;
}
long long get_num2(long long l,long long r){
l--;
l%=mod;r%=mod;
return (r*(r+1)%mod*(2*r+1)%mod+mod-l*(l+1)%mod*(2*l+1)%mod)%mod*inv6%mod;
}
long long ksm(long long x,long long t){
long long tot=1;
while(t){
if(t&1) (tot*=x)%=mod;
(x*=x)%=mod;
t/=2;
}
return tot;
}
void get_mu_dot_id(){
int temp=0;
sum[1]=key[1]=1;
for(int i=2;i<=bound;i++){
if(!vis[i]){p[++p[0]]=i;key[i]=mod-i*i%mod;}
for(int j=1;j<=p[0] && (temp=i*p[j])<=bound;j++){
vis[temp]=true;
if(i%p[j]==0) break;
key[temp]=key[i]*key[p[j]]%mod;
}
sum[i]=sum[i-1]+key[i];
}
}
long long F(long long x){
if(x<=bound) return sum[x];
if(tf[n/x]) return op[n/x];tf[n/x]=true;
long long ans=1,l=2,r;
while(l<=x) {r=x/(x/l);(ans+=mod-get_num2(l,r)*F(x/l)%mod)%=mod;l=r+1;}
return op[n/x]=ans;
}
long long sqr(long long x){
return x*x%mod;
}
long long get_we(long long x){
if(x<=bound) return sum[x];
return op[n/x];
}
long long get_ans(long long x){
long long l=1,r;
long long ans=0;
while(l<=x){
r=x/(x/l);
(ans+=get_num(l,r)*get_we(x/l)%mod)%=mod;
l=r+1;
}
return ans;
}
void solve(){
long long ans=0,last=0,now,l=1,r;
while(l<=n){
r=n/(n/l);now=get_ans(r);
(ans+=(now+mod-last)%mod*sqr(get_num(1,n/l)))%=mod;
l=r+1;last=now;
}
printf("%lld\n",ans);
}
int main(){
scanf("%lld",&n);
inv2=ksm(2,mod-2);inv6=ksm(6,mod-2);
get_mu_dot_id();F(n);solve();
}
4.石像
唯一没有写程序的题,因为我现在还想不明白那个dp。
也是简单的反演。
给出,
求
第一眼看上去,好神仙啊,居然还有这种计数问题。
直接莫比乌斯反演。
两个东西都是可以min_25的。
因为前面那个函数在x=prime的时候,值都是64,x=prime^k的时候,值就是,所以直接min_25。
就不用说了吧。
然后每一次都是根号去求狄利克雷卷积前缀和。
复杂度也是对的。
但是后面那个东西怎么求啊,dp?不太懂。(坑
本文深入探讨了数论算法中的核心概念,包括类欧几里得算法、中国剩余定理、狄利克雷卷积与反演等,通过具体例题解析了如何应用这些理论解决复杂的数学问题。
3万+

被折叠的 条评论
为什么被折叠?



