• 【51nod1847】奇怪的数学题


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

    Solution

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

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

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

    [egin{aligned} sumlimits_{i=1}^{n}sumlimits_{j=1}^{n}sgcd(i,j)^k&=sumlimits_{i=1}^nsumlimits_{j=1}^{n}f(gcd(i,j))^k\ &=sumlimits_{d=1}^{n}f(d)^ksumlimits_{i=1}^{n}sumlimits_{j=1}^{n}[gcd(i,j)=d]\ &=sumlimits_{d=1}^{n}f(d)^ksumlimits_{i=1}^{lfloor frac{n}{d} floor}sumlimits_{j=1}^{lfloor frac{n}{d} floor}[gcd(i,j)=1]\ &=sumlimits_{d=1}^{n}f(d)^kcdot(2sumlimits_{i=1}^{lfloor frac{n}{d} floor}varphi(i)-1) end{aligned} ]

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

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

    [sumlimits_{d=1}^{n}f(d)^kcdot(2sumlimits_{i=1}^{lfloor frac{n}{d} floor}varphi(i)-1) ]

      这个东西

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

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

    ​   

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

      首先定义

    [G_i=sumlimits_{j=2}^{i}f(j)^K ]

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

    [egin{aligned} &s(x)=x^K\ &g_{i,j}=sumlimits_{k=1}^{i}[kin Prime或minp(k)>P_j]s(k) end{aligned} ]

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

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

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

      

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

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

    [egin{aligned} sumlimits_{i=1}^{n}i^K&=sum_{i=1}^nsum_{j=1}^Kegin{Bmatrix}K\jend{Bmatrix}i^underline{j}\ &=sum_{j=1}^Kegin{Bmatrix}K\jend{Bmatrix}sum_{i=1}^ni^underline{j}\ &=sum_{j=1}^Kegin{Bmatrix}K\jend{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,&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
    ",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)];
    		}
    	}
    }
    
  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1079:计算分数加减表达式的值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1078:求分数序列和
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1077:统计满足条件的4位数
    晕牛【拓扑排序】【BFS】
    过路费【Floyd】
    电视游戏问题【DP】【背包】
    音乐节拍【模拟】
    密码【高精】
  • 原文地址:https://www.cnblogs.com/yoyoball/p/9204846.html
Copyright © 2020-2023  润新知