【51nod1847】奇怪的数学题

Portal -->51nod1847奇怪的数学题

Solution

  这个的话。。看到跟\(gcd\)相关这种东西。。那肯定是要先反演一波啦

  \(sgcd(i,j)\)这个东西看起来很麻烦不过其实就是\(gcd(i,j)\)的次大约数

  为了方便表示我们用\(f(x)\)表示\(x\)的次大约数,那么原来的式子可以写成:

\[\begin{aligned} \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}sgcd(i,j)^k&=\sum\limits_{i=1}^n\sum\limits_{j=1}^{n}f(gcd(i,j))^k\\ &=\sum\limits_{d=1}^{n}f(d)^k\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=d]\\ &=\sum\limits_{d=1}^{n}f(d)^k\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{n}{d}\rfloor}[gcd(i,j)=1]\\ &=\sum\limits_{d=1}^{n}f(d)^k\cdot(2\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\varphi(i)-1) \end{aligned} \]

  然后倒数第二部到最后一步是直接由\(\phi\)的定义得到的嗯(然后我就特别弱智地用\(\mu\)推了好久还要推错了。。)

  然后现在我们就是要求出:

\[\sum\limits_{d=1}^{n}f(d)^k\cdot(2\sum\limits_{i=1}^{\lfloor \frac{n}{d}\rfloor}\varphi(i)-1) \]

  这个东西

  后半部分的话是经典的杜教筛求欧拉函数前缀和,然后因为\(i\)的上界是\(\lfloor \frac{n}{d}\rfloor\)所以求出前缀和以后分块搞一波就好了

  关于杜教筛求欧拉函数前缀和。。具体一点的话。。。戳这里好了

​   

  然后前半部分的求法十分神仙(疯狂%%%),为了防止变量名称弄混,统一一下接下来的部分题目中给出的指数为\(K\),而用来枚举的变量为\(k\)

  首先定义

\[G_i=\sum\limits_{j=2}^{i}f(j)^K \]

  然后我们令min_25中的那个\(g\)数组按照如下的定义计算:

\[\begin{aligned} &s(x)=x^K\\ &g_{i,j}=\sum\limits_{k=1}^{i}[k\in Prime或minp(k)>P_j]s(k) \end{aligned} \]

  然后我们思考一下在计算过程中,\(g_{i,j}=g_{i,j-1}-s(P_j)\cdot(g_{\lfloor\frac{i}{P_j}\rfloor,j-1}-g_{P_j-1,j-1})\)这一步中,括号中的那一部分的含义
  其实就是\(\sum\limits_{x}s(x)[minp(x)=P_j且minp(\frac{x}{P_j})>=P_j]\),然后这个其实就是这堆满足条件的\(x\)\(f(x)^k\)之和

  然后又因为min_25筛中,每个数只会被考虑一次,所以我们直接多开一个数组把这堆东西加起来就好了

  但是这并没有算上\(k\in Prime\)的情况,所以此外我们还要单独算一下素数情况下的取值,其实也就是素数的个数(因为\(f(prime)=1\)),这个也是直接用min_25筛一下就好了

  

  最后还剩下一个小问题,\(g_{i,0}\)在初始化的时候要快速求出\(\sum\limits_{j=1}^{i}j^k\)

  注意到\(k\)很小,那么我们用第二类斯特林数处理一下:

\[\begin{aligned} \sum\limits_{i=1}^{n}i^K&=\sum_{i=1}^n\sum_{j=1}^K\begin{Bmatrix}K\\j\end{Bmatrix}i^\underline{j}\\ &=\sum_{j=1}^K\begin{Bmatrix}K\\j\end{Bmatrix}\sum_{i=1}^ni^\underline{j}\\ &=\sum_{j=1}^K\begin{Bmatrix}K\\j\end{Bmatrix}\frac{{(n+1)}^\underline{j+1}}{j+1} \end{aligned} \]

  然后就很愉快滴解决啦

  

  一些实现的细节(你别说个人感觉因为那个模数不是很友善所以写起来有点麻烦。。)

1、取模的话可以自然溢出,\(unsigned\ int\)了解一下ovo

2、求下降阶乘幂的时候,因为这题的模数不是质数,所以不能快乐求逆元,但是因为\(k\)比较小所以直接暴力一波,一个一个数乘,这堆数中必定有一个是\(j+1\)的倍数,所以可以直接先除再乘就好了,注意判断的时候每次都取模判断的话会愉快T掉,所以应该在一开始的时候先把余数算出来,然后根据余数的加法定理直接判就好了

  

  代码的话大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#define ll long long
#define ui unsigned int
using namespace std;
const int MAXN=1e5+10;
map<ll,ui>Rec;
ui P[MAXN],loc1[MAXN],loc2[MAXN];
ui g[MAXN*2],rec[MAXN*2],sphi[MAXN],pcnt[MAXN*2],pw[MAXN*2],G[MAXN*2];
ui S[60][60];
bool vis[MAXN];
ll n,m,K,cnt,cntval,sq;
ui ans;
void prework(int n);
void init_loc(ll n);
void get_g();
int Pos(ll x){return x<=sq?loc1[x]:loc2[n/x];} 
ui Sum(ui l,ui r);
ui SumPhi(ll n);
ui calc_sumk(ll n);
ui ksm(ui x,ll y);
ui f(ll x){return G[Pos(x)]+pcnt[Pos(x)];}

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	scanf("%lld%lld\n",&n,&K);
	prework(MAXN);
	sq=sqrt(n);
	m=upper_bound(P+1,P+1+cnt,sq)-P-1;
	init_loc(n);
	get_g();
	ans=0;
	ui pre=0,now;
	for (ll i=2,pos;i<=n;i=pos+1){
		pos=n/(n/i);
		now=f(i);
		ans+=(SumPhi(n/i)*2-1)*(now-pre);
		pre=now;
	}
	printf("%lu\n",ans);
}

ui ksm(ui x,ll y){
	ui ret=1,base=x;
	for (;y;y>>=1,base=base*base)
		if (y&1) ret=ret*base;
	return ret;
}

void prework(int n){
	cnt=0;
	sphi[1]=1;
	for (int i=2;i<=n;++i){
		if (!vis[i])
			P[++cnt]=i,sphi[i]=i-1;
		for (int j=1;j<=cnt&&P[j]*i<=n;++j){
			vis[i*P[j]]=true;
			if (i%P[j]==0){
				sphi[i*P[j]]=sphi[i]*P[j];
				break;
			}
			else
				sphi[i*P[j]]=sphi[i]*sphi[P[j]];
		}
	}
	for (int i=1;i<=n;++i) sphi[i]=sphi[i-1]+sphi[i];
	S[0][0]=1;
	for (int i=1;i<=K;++i){
		S[i][1]=1;
		for (int j=2;j<=i;++j)
			S[i][j]=S[i-1][j]*(ui)j+S[i-1][j-1];
	}
	for (int i=1;i<=cnt;++i) pw[i]=ksm(P[i],K);
}

void init_loc(ll n){
	cntval=0;
	for (ll i=1,pos;i<=n;i=pos+1){
		rec[++cntval]=n/i;
		pos=n/(n/i);
	}
	reverse(rec+1,rec+1+cntval);
	for (int i=1;i<=cntval;++i)
		if (rec[i]<=sq) loc1[rec[i]]=i;
		else loc2[n/rec[i]]=i;
}

ui Sum(ui l,ui r){
	ui tmp1=l+r,tmp2=r-l+1;
	if (tmp1%2==0) tmp1/=2;
	else tmp2/=2;
	return tmp1*tmp2;
}

ui SumPhi(ll n){
	if (n<MAXN) return sphi[n];
	map<ll,ui>::iterator tmp=Rec.find(n);
	if (tmp!=Rec.end()) return tmp->second;
	ui ret=Sum(1,n);
	for (ll i=2,pos;i<=n;i=pos+1){
		pos=n/(n/i);
		ret-=SumPhi(n/i)*(pos-i+1);
	}
	Rec[n]=ret;
	return ret;
}

ui calc_sumk(ll n){
	ui ret=0,tmpval;
	int tmp;
	for (int i=1;i<=K;++i){
		tmpval=1;
		tmp=(n-i+1)%(i+1);
		for (ll j=n-i+1;j<=n+1;++j,++tmp){
			if (tmp>=i+1) tmp-=i+1;
			if (!tmp) tmpval*=(ui)j/(i+1);
			else tmpval*=(ui)j;
		}
		ret+=S[K][i]*tmpval;
	}
	return ret;
}

void get_g(){
	for (int i=2;i<=cntval;++i) g[i]=calc_sumk(rec[i])-1,pcnt[i]=rec[i]-1,G[i]=0;
	for (int j=1;j<=m;++j){
		for (int i=cntval;i>=1&&rec[i]>=1LL*P[j]*P[j];--i){
			g[i]-=pw[j]*(g[Pos(rec[i]/P[j])]-g[Pos(P[j]-1)]);
			pcnt[i]-=pcnt[Pos(rec[i]/P[j])]-pcnt[Pos(P[j]-1)];
			G[i]+=g[Pos(rec[i]/P[j])]-g[Pos(P[j]-1)];
		}
	}
}
posted @ 2018-06-20 17:19  yoyoball  阅读(1407)  评论(0编辑  收藏  举报