苏州省选集训营第一天课件解析

本文深入探讨了数论算法中的核心概念,包括类欧几里得算法、中国剩余定理、狄利克雷卷积与反演等,通过具体例题解析了如何应用这些理论解决复杂的数学问题。

      回头看看自己真菜。      ---题记

正题

      1.类欧几里得算法

      2.中国剩余定理

      3.简单的数论方法

      4.狄利克雷卷积与反演

例题

      1.一个人的数论:

      给出n的质因数分解:n=\prod_{i=1}^{w} p_i^{a_i},求\sum_{i=1}^{n}[gcd(i,n)==1] i^m\mod 10^9+7

      p_i,a_i<=10^9,w<=1000,m<=100

      这条式子卷上mu。

      \\=\sum_{i=1}^{m}i^m\sum_{d|i,d|n}\mu(d) \\=\sum_{d|n}\mu(d)d^m\sum_{i=1}^{\frac{n}{d}} i^m

      然后我们发现后面那个sigma的值只跟\frac{n}{d}有关,因为他是一个m+1次函数,这个可以很容易就想到,因为

      \\1+...+n=\frac{n(n+1)}{2} \\1+4+9+...+n^2=\frac{n(n+1)(2n+1)}{6}

      那么他就可以用m+2个点来确定,直接n^2拉格朗日插值法求出这个函数每一项的系数。

      假设第i位的系数位f_i,那么原来的式子就等于。

      \\=\sum_{d|n} \mu(d)d^m\sum_{i=0}^{m+1}f_i(\frac{n}{d})^i \\=\sum_{i=0}^{m+1}f_i\sum_{d|n}\mu(d)d^m(\frac{n}{d})^i

      那么现在我们再来考虑后面这个式子,我们设g(d)=\mu(d)d^m明显是一个积性函数,h(d)=d^i也明显是一个积性函数。

      那么后面那个sigma相当于求(g*h)(n),*为狄利克雷卷积。

      两个积性函数的狄利克雷卷积是个积性函数,所以我们只需要求出这个东西分别在p_1^{a_1},p_2^{a_2}...,p_w^{a_w}下的值就好了。

      对于每一个p_i^{a_i},我们考虑g不为0的位置只有1,p_i所以算这个东西就是O(1)的。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.怎样跑得更快

      给定n<=10^5;c,d<=10^9,此题所有式子对998244353取膜,现有序列b满足:

      b_i\equiv\sum_{j=1}^{n}gcd(i,j)^clcm(i,j)^dz_j,求序列z。

      首先我们可以把题目转化一下,变成

      \\b_i\equiv\sum_{j=1}^ngcd(i,j)^{c-d}i^dj^dz_j \\b_ii^{-d}\equiv\sum_{j=1}^n gcd(i,j)^{c-d}z_j^j^d

      我们令x_i=b_ii^{-d},y_i=z_ii^d,w=c-d,那么原来的式子就变成了。

      x_i\equiv\sum_{j=1}^{n}gcd(i,j)^wy_j

      现在就是知道了x,求y。

      莫比乌斯反演一下:

      \\x_i\equiv\sum_{g|i}g^w\sum_{j=1}^{\frac{n}{g}}[gcd(\frac{i}{g},j)=1]y_{jg} \\x_i\equiv\sum_{g|i} g^w\sum_{j=1}^{\frac{n}{g}}y_{jg}\sum_{d|j,d|\frac{i}{g}}\mu(d) \\x_i\equiv\sum_{g|i}g^w\sum_{d|\frac{i}{g}}\mu(d)\sum_{j=1}^{\frac{n}{gd}}y_{jgd}

      我们令T=gd,那么前两个考虑当T一定的时候,g,d必定是T的约数且相乘等于T,那么就相当于求狄利克雷卷积的第T项。

      x_i\equiv\sum_{T|i} (id^w*\mu)(T)\sum_{j=1}^{\frac{n}{T}} y_{jT}

      我们把后面那坨东西叫做f(T)\equiv(id^w*\mu)(T)\sum_{j=1}^{\frac{n}{T}} y_{jT}

      那么我们现在就知道了x_i\equiv\sum_{T|i}f(T)

      这个像啥,莫比乌斯反演

      f(T)\equiv \sum_{i|T}\mu(i)x_{\frac{T}{i}}

      那么我们就知道f了。

      发现前面那个东西(id^w*\mu)(T)是可以线性筛+狄利克雷卷积(或者不用也行),搞出来的。

      我们把它放到左边f(T)((id^w*\mu)(T))^{-1}\equiv\sum_{j=1}^{\frac{n}{T}}y_{jT}

      相当于什么?

      f(T)((id^w*\mu)(T))^{-1}\equiv\sum_{T|j}y_j!!!

      这不是另外一个莫比乌斯反演式子吗?

      y_j\equiv\sum_{j|T}\mu(j)f(\frac{T}{j})((id^w*\mu)(\frac{T}{j}))^{-1}

      做完了?退回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.给出n<=10^{10},求\sum_{i=1}^n\sum_{j=1}^{n}lcm(i,j)

      很明显要转化成gcd然后莫比乌斯反演。

      \\=\sum_{g=1}^{n}g\sum_{i=1}^{\frac{n}{g}}\sum_{j=1}^{\frac{n}{g}}[gcd(i,j)==1]ij \\=\sum_{g=1}^{n}g\sum_{d=1}^{\frac{n}{g}}\mu(d)d^2\sum_{i=1}^{\frac{n}{dg}}\sum_{j=1}^{\frac{n}{dg}}ij \\=\sum_{T=1}^n(id*(\mu(d)\dot\ id^2))(T)(\sum_{i=1}^{\frac{n}{T}}i)^2

      前面\mu(d)\dot\ id^2可以用n^{\frac{2}{3}}的时间来求出\frac{n}{1},\frac{n}{2},...,\frac{n}{n}的前缀和。(其中有一些值是相等的,一共有2\sqrt n个不同的值。

      然后每一次用根号时间求一下前面那个式子的前缀和,时间复杂度就是(\sqrt 1+\sqrt 2 + ...+\sqrt n)*2积分一下???即是正确的复杂度。

      网上没有题,所以自己写了一个对拍,数据包和标程。小数据都是对的,如果有哪位神仙发现这份代码有问题,请咨询蒟蒻。

#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。

      也是简单的反演。

      给出n<=20,m<=10^{10},k<=n(n-1),x_i,y_i<=n,i,j\in[1,k]

     求\sum_{a_1=1}^m\sum_{a_2=1}^m...\sum_{a_n=1}^m\sigma_0(gcd(a_1,a_2,...,a_n)^3)^3\prod_{i=1}^{k}[a_{x_i}<=a_{y_i}]

      第一眼看上去,好神仙啊,居然还有这种计数问题。

     直接莫比乌斯反演。

      \sum_{g=1}^{m}\sigma_0(g^3)^3\sum_{d=1}^{\frac{m}{g}}\mu(d)\sum_{a_1=1}^{\frac{m}{dg}}\sum_{a_2=1}^{\frac{m}{dg}}...\sum_{a_n=1}^{\frac{m}{dg}}\prod_{i=1}^{k}[a_{x_i}<=a_{y_i}] \\=\sum_{T=1}^{m}(\sigma_0(id^3)^3*\mu)(T)\sum_{a_1=1}^{\frac{m}{T}}\sum_{a_2=1}^{\frac{m}{T}}...\sum_{a_n=1}^{\frac{m}{T}}\prod_{i=1}^{k}[a_{x_i}<=a_{y_i}]

      两个东西都是可以min_25的。

      因为前面那个函数在x=prime的时候,值都是64,x=prime^k的时候,值就是(3k+1)^3,所以直接min_25。

      \mu就不用说了吧。

      然后每一次都是根号去求狄利克雷卷积前缀和。

      复杂度也是对的。

      但是后面那个东西怎么求啊,dp?不太懂。(坑

 

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值