[loj#572][杜教筛][min-25筛]Misaka Network 与求和

1 篇文章 0 订阅
1 篇文章 0 订阅

Description

传送门

题解

强行拼题来着啊2333
推一发柿子可以得到一个线性的做法就是
a n s = ∑ i = 1 n f ( i ) ∗ ( ∑ j = 1 n i ϕ ( j ) ∗ 2 − 1 ) ans=\sum_{i=1}^{n}f(i)*(\sum_{j=1}^{\frac{n}{i}}\phi(j)*2-1) ans=i=1nf(i)(j=1inϕ(j)21)
然后就不会了啊qwq
如果设 s u m ( x ) = ∑ i = 1 x ϕ ( i ) sum(x)=\sum_{i=1}^{x}\phi(i) sum(x)=i=1xϕ(i),那么其实后面那项就是
( s u m ( ⌊ n i ⌋ ) ∗ 2 − 1 ) (sum(\lfloor\frac{n}{i}\rfloor)*2-1) (sum(in)21)
那么如果能快速算出 f ( x ) f(x) f(x)的前缀和,我们就可以对后面数论分块套个杜教筛玩了qwq
这个东西是可以用类似min-25筛的东西做出来的
我们考虑一个数 x x x,他要不就是质数,要不他至少有一个因子是小于 N \sqrt N N 的,且最大质因数不小于 N \sqrt N N
所以我们考虑在 N \sqrt N N 的上界从大往小枚举开筛
不妨设 S ( n , m ) S(n,m) S(n,m)表示对于一类上界为 n n n的数,次小质因子至少在 m m m以上的数的 f f f
转移如下
S ( n , m ) = ∑ j &gt; = m p r [ j ] 2 &lt; = n ∑ k = 1 p r [ j ] k + 1 &lt; = n S ( n p r [ j ] k , j + 1 ) + p r [ j ] k ∑ i = p r [ j ] n p r [ j ] k [ i is a prime ] S(n,m)=\sum_{j&gt;=m}^{pr[j]^2&lt;=n}\sum_{k=1}^{pr[j]^{k+1}&lt;=n}S(\frac{n}{pr[j]^k},j+1)+pr[j]^k\sum_{i=pr[j]}^{\frac{n}{pr[j]^k}}[\text{i is a prime}] S(n,m)=j>=mpr[j]2<=nk=1pr[j]k+1<=nS(pr[j]kn,j+1)+pr[j]ki=pr[j]pr[j]kn[i is a prime]
解释一下
对于前面一个 S S S的转移,我们理解为他并非次大质因数,故我们枚举他的次幂转移
对于后面一个转移,相当于他是次大质因数,此时一定有一个比他大的质数作为最大质因子,所以后面一段实际上是一个质数个数和,用min-25筛简单预处理即可
似乎还是可以做次小质因子的贡献的23333
以后出来毒一下qwq

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#include<unordered_map>
#define LL long long
#define ui unsigned int
#define mp(x,y) make_pair(x,y)
#define pui pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(ui x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(ui x){write(x);putchar(' ');}
inline void pr2(ui x){write(x);putchar('\n');}
const int MAXN=2000005;
const int MAXM=90005;
int pr[MAXN],pre[MAXN],plen,K;
bool is[MAXN];
ui phi[MAXN],pw[MAXN];
ui pow_mod(ui a,int b)
{
	ui ret=1;
	while(b)
	{
		if(b&1)ret=ret*a;
		a=a*a;b>>=1;
	}
	return ret;
}
void init()
{
	phi[1]=1;
	for(int i=2;i<MAXN;i++)
	{
		if(!is[i])pr[++plen]=i,phi[i]=i-1,pw[plen]=pow_mod(i,K);
		for(int j=1;j<=plen&&i*pr[j]<MAXN;j++)
		{
			is[i*pr[j]]=true;
			if(!(i%pr[j])){phi[i*pr[j]]=phi[i]*pr[j];break;}
			phi[i*pr[j]]=phi[i]*(pr[j]-1);
		}
	}
	for(int i=2;i<MAXN;i++)phi[i]+=phi[i-1],pre[i]=pre[i-1]+(!is[i]);
}
ui id[MAXN],n,N;
int getid(int x){return x<=N?x:n/x+N;}
ui solvephi(int x)
{
	if(x<MAXN)return phi[x];
	int u=getid(x);
	if(id[u]!=-1)return id[u];
	ui ret=1LL*(x+1)*x/2;
	for(int i=2,nxt;i<=x;i=nxt+1)
	{
		nxt=x/(x/i);
		ret-=(nxt-i+1)*solvephi(x/i);
	}
	return id[u]=ret;
}
int id1[MAXM],g[MAXM],ps[MAXM],NN;
int getid1(int x,int li){return x<=NN?x:li/x+NN;}
int getpr(int n)//质数个数和 
{
	if(n<=1)return 0;
	int m=0;NN=sqrt(n);
	for(int i=1,nxt;i<=n;i=nxt+1)
	{
		nxt=n/(n/i);int u=n/i;
		if(u<=NN)id1[u]=++m;else id1[n/u+NN]=++m;
		g[m]=u-1;ps[m]=u;
	}
	for(int j=1;pr[j]<=NN;j++)
		for(int i=1;i<=m;i++)
		{
			if(pr[j]*pr[j]>ps[i])break;
			g[i]-=(g[id1[getid1(ps[i]/pr[j],n)]]-pre[pr[j-1]]);
		}
	return g[1];
}
unordered_map<int,ui> mp[MAXM];
ui sum[MAXN];
ui S(int n,int m)
{
	if(n<2||pr[m]>n)return 0;
	if(mp[m][n])return mp[m][n];
	ui ret=0;
	for(int j=m;j<=plen&&1LL*pr[j]*pr[j]<=n;j++)
	{
		int lin=pr[j];
		for(int k=1;1LL*lin*pr[j]<=n;k++)
		{
			ret+=S(n/lin,j+1);
			ret+=pw[j]*(sum[getid(n/lin)]-j+1);
			lin*=pr[j];
		}
	}
	return mp[m][n]=ret;
}
ui ph[MAXN],us[MAXN];
int main()
{
	memset(id,-1,sizeof(id));
	n=read();K=read();N=sqrt(n);
	init();
	getpr(n);
	for(int i=1,nxt;i<=n;i=nxt+1)
	{
		nxt=n/(n/i);int u=n/i;
		sum[getid(u)]=g[id1[getid1(u,n)]];
		ph[getid(u)]=solvephi(u);
	}
	ui ans=0;ui lst=0;
	for(int i=1,nxt;i<=n;i=nxt+1)
	{
		nxt=n/(n/i);int u=n/i;
		ui num=S(nxt,1)+g[id1[getid1(nxt,n)]];
		ui s1=ph[getid(u)]*2-1;
		ui s2=num-lst;
		lst=num;
		ans+=s1*s2;
	}
	pr2(ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值