【LOJ6207】米缇(莫比乌斯反演)(杜教筛)(拉格朗日插值)

传送门


题解:

杜教筛里面long long 没有取模炸了一堆。。。

首先推式子:

A n s = ∑ i = 1 n ∑ j = 1 n σ k ( i j ) = ∑ i = 1 n ∑ j = 1 n ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] ( i y x ) k = ∑ d = 1 n μ ( d ) d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ ∑ x ∣ i ∑ y ∣ j ( i y x ) k = ∑ d = 1 n μ ( d ) d k ∑ i = 1 ⌊ n d ⌋ ∑ x ∣ i ( i x ) k ∑ j = 1 ⌊ n d ⌋ ∑ y ∣ j y k = ∑ d = 1 n μ ( d ) d k ( ∑ i = 1 ⌊ n d ⌋ i k ⌊ n i d ⌋ ) 2 \begin{aligned} Ans&=&&\sum_{i=1}^{n}\sum_{j=1}^n\sigma_k(ij)\\ &=&&\sum_{i=1}^n\sum_{j=1}^n\sum_{x\mid i}\sum_{y\mid j}[gcd(x,y)=1](\frac{iy}{x})^k\\ &=&&\sum_{d=1}^n\mu(d)d^k\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{x\mid i}\sum_{y\mid j}(\frac{iy}{x})^k\\ &=&&\sum_{d=1}^n\mu(d)d^k\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{x\mid i}(\frac{i}{x})^k\sum_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{y\mid j}y^k\\ &=&&\sum_{d=1}^n\mu(d)d^k(\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}i^k\lfloor\frac{n}{id}\rfloor)^2 \end{aligned} Ans=====i=1nj=1nσk(ij)i=1nj=1nxiyj[gcd(x,y)=1](xiy)kd=1nμ(d)dki=1dnj=1dnxiyj(xiy)kd=1nμ(d)dki=1dnxi(xi)kj=1dnyjykd=1nμ(d)dk(i=1dnikidn)2

对于 μ ⋅ I d k \mu\cdot Id_k μIdk,我们利用 I d k Id_k Idk可以直接构造杜教筛

对于后面的 S σ k S_{\sigma_k} Sσk,可以直接筛一部分后暴力做。

对于 I d k Id_k Idk,直接用拉格朗日插值法算一算就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=1e9+7;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}
inline int MD(ll a){return a<0?(a<-mod?(a%mod+mod)%mod:a+mod):(a>=mod?a%mod:a);}

cs int N=1e7,K=7e3,SIZE=N+5;

ll n;int k,lim;

bool mark[SIZE];
int dk[SIZE],mu[SIZE],dm[SIZE],pw[SIZE];
std::vector<int> prime;
int fac[K+5],ifac[K+5];

inline void init(int len){lim=len;
	mu[1]=pw[1]=dm[1]=dk[1]=1;
	for(int re i=2;i<=len;++i){
		if(!mark[i]){
			prime.push_back(i);
			dm[i]=1;
			pw[i]=power(i,k);
			dk[i]=add(pw[i],1);
			mu[i]=mod-1;
		}
		for(int re j:prime){
			if(i*j>len)break;
			mark[i*j]=true;
			pw[i*j]=mul(pw[i],pw[j]);
			if(i%j){
				dm[i*j]=i;
				mu[i*j]=dec(0,mu[i]);
				dk[i*j]=mul(dk[i],dk[j]);
			}
			else {
				dm[i*j]=dm[i];
				dk[i*j]=dm[i]==1?add(1,mul(dk[i],pw[j])):mul(dk[i/dm[i]*j],dk[dm[i]]);
				break;
			}
		}
	}
	for(int re i=2;i<=len;++i){
		mu[i]=add(mu[i-1],mul(mu[i],pw[i]));
		Inc(dk[i],dk[i-1]);
		Inc(pw[i],pw[i-1]);
	}
	fac[0]=fac[1]=ifac[0]=1;
	for(int re i=2;i<=k+1;++i)fac[i]=mul(fac[i-1],i);
	ifac[k+1]=power(fac[k+1],mod-2);
	for(int re i=k;i;--i)ifac[i]=mul(ifac[i+1],i+1);
}

struct Map{
	static cs int magic=18985739;
	ll key[magic];
	int val[magic],tmp;
	Map(){memset(key,-1,sizeof key);}
	inline int &operator[](ll k){
		int h=k%magic;
		while(key[h]!=-1&&key[h]!=k)h=h+1==magic?0:h+1;
		if(key[h]==-1){key[h]=k;val[h]=0;}
		return val[h];
	}
	inline cs int &operator[](ll k)cs{
		int h=k%magic;
		while(key[h]!=-1&&key[h]!=k)h=h+1==magic?0:h+1;
		return key[h]==-1?tmp:val[h];
	}
	inline bool find(ll k)cs{
		int h=k%magic;
		while(key[h]!=-1&&key[h]!=k)h=h+1==magic?0:h+1;
		return key[h]==k;
	}
}sum_k;

int pre[K+5],suf[K+5];
inline int Sum_k(ll n){
	if(n<=lim)return pw[n];
	if(sum_k.find(n))return sum_k[n];
	pre[0]=suf[k+3]=1;int t=n%mod;
	for(int re i=1;i<=k+2;++i)pre[i]=mul(pre[i-1],dec(t,i));
	for(int re i=k+2;i>=1;--i)suf[i]=mul(suf[i+1],dec(t,i));
	int ans=0;
	for(int re i=1;i<=k+2;++i){
		int coef=mul(mul(pre[i-1],suf[i+1]),mul(ifac[i-1],ifac[k+2-i]));
		(k^i)&1?Dec(ans,mul(coef,pw[i])):Inc(ans,mul(coef,pw[i]));
	}
	return sum_k[n]=ans;
}

inline int Sum_dk(ll n){
	if(n<=lim)return dk[n];
	int ans=0;
	for(ll i=1,j,last=0,now;i<=n;i=j+1){
		now=Sum_k(j=n/(n/i));
		Inc(ans,mul(dec(now,last),MD(n/i)));
		last=now;
	}
	return ans;
}

bool vis[100005];
int f[100005];
inline int Sum_mu(ll n){
	if(n<=lim)return mu[n];
	if(vis[::n/n])return f[::n/n];
	vis[::n/n]=true;
	int ans=1;
	for(ll re i=2,j,last=1,now;i<=n;i=j+1){
		now=Sum_k(j=n/(n/i));
		Dec(ans,mul(dec(now,last),Sum_mu(n/i)));
		last=now;
	}
	return f[::n/n]=ans;
}

signed main(){
//	freopen("miti.in","r",stdin);
	std::cin>>n>>k;
	init(std::min(N*1.,std::max(k*1.+1,pow(n,2./3.))));
	int ans=0;
	for(ll re i=1,j,last=0,now;i<=n;i=j+1){
		j=n/(n/i);
		int sum=Sum_dk(n/i);
		now=Sum_mu(j);
		Inc(ans,mul(dec(now,last),mul(sum,sum)));
		last=now;
	}
	cout<<ans<<"\n";
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值