zoukankan      html  css  js  c++  java
  • 【BZOJ2627】JZPKIL(数论大杂烩)

    点此看题面

    大致题意:(sum_{i=1}^ngcd(i,n)^xlcm(i,n)^y)

    前言

    该怎么说呢?不管怎样,至少最近在数论方面还是有点长进的吧,除了伯努利数那个地方实在是不知道,其他式子都是自己一步一步推出来的。

    尽管这可能很大程度是归功于做过一道这题的简单版:【BZOJ3601】一个人的数论。。。(那道题十分良心,题目中给出的是(n)的质因数分解,不需要(Pollard-Rho)

    这是一道数论大杂烩,涉及到莫比乌斯反演伯努利数积性函数(Pollard-Rho)(说到它,自然也包含(MillerRabin))等众多知识,请做好心理准备。

    第一次(大概吧)能把数论题的代码写到(100)行。。。

    推式子+各种准备工作共计半小时,写代码一个小时,调试半小时,写博客又快一个小时,共计三小时。。。

    莫比乌斯反演

    众所周知,(gcd(i,n)cdot lcm(i,n)=icdot n),因此我们完全可以去除(lcm(i,n)),得到:

    [sum_{i=1}^ngcd(i,n)^{x-y}(icdot n)^y ]

    我们把(n^y)提前。看到有个(gcd),于是就开始喜闻乐见地枚举(gcd)

    [n^ysum_{d|n}d^{x-y}sum_{i=1}^{frac nd}(icdot d)^y[gcd(i,frac nd)=1]=n^ysum_{d|n}d^xsum_{i=1}^{frac nd}i^y[gcd(i,frac nd)=1] ]

    (sum_{d|n}mu(d)=[n=1]),得(sum_{p|i,p|frac nd}mu(p)=1),然后枚举(p)得到:

    [n^ysum_{d|n}d^xsum_{p|frac nd}mu(p)p^ysum_{i=1}^{frac n{dp}}i^y ]

    在这个式子中,你看到了什么?没错,是它,是它,就是它,(sum_{i=1}^{frac n{dp}}i^y)

    它来了,自然数幂和,它来了!

    在上面给出的那道题目中,同样需要自然数幂和。但由于它的次数(d)比较小,可以(O(d^3))高斯消元。

    但这道题(yle3000)我觉得它就是在难为我高斯消元,需要采用一些更高级的东西。

    自然数幂和

    考虑我们设(S(n)=sum_{i=1}^ni^y)

    这里引入一个叫伯努利数的东西,它的定义是(B_0=1),且满足:

    [sum_{i=0}^nC_{n+1}^iB_i=0 ]

    由它的性质实际上我们只要把(B_n)移到不等式一边,就可以得到递推式:

    [B_n=-frac1{n+1}sum_{i=0}^{n-1}C_{n+1}^iB_i ]

    然后有一个叫做伯努利公式的东西,长这副样子:(注意,这里(B_i)正伯努利数,而上面的(B_1<0),此处要取绝对值)

    [sum_{i=1}^ni^k=frac1{k+1}sum_{i=0}^kC_{k+1}^iB_in^{k-i+1} ]

    你会发现这个公式和前面两个式子长得特别像(感觉就是二者的融合版本,然后乘个带(n)的项),不过我也没去深究它的证明。。。

    把这个公式套到我们的(S(n))上,得到:(其实就是把(k)换成(y),然后把(frac1{y+1})移到了里面。。。)

    [S(n)=sum_{i=0}^yfrac1{y+1}C_{y+1}^iB_in^{y-i+1} ]

    我们令(a_i=frac1{y+1}C_{y+1}^iB_i),简化原式得到:

    [S(n)=sum_{i=0}^ya_in^{y-i+1} ]

    积性函数

    我们把(S(n))代回原式,得到:

    [n^ysum_{d|n}d^xsum_{p|frac nd}mu(p)p^ysum_{i=0}^ya_i(frac n{dp})^{y-i+1} ]

    然后我们提前枚举(i),并令(D=dp),得到:

    [n^ysum_{i=0}^ya_isum_{D|n}(frac nD)^{y-i+1}sum_{d|D}d^x(frac Dd)^ymu(frac Dd) ]

    显然(dcdot(frac Dd)=D),我们把它们拼起来,然后又发现((frac nD)cdot D=n),于是接着拼起来,最终得到:

    [n^ysum_{i=0}^ya_in^{y-i+1}sum_{D|n}D^{i-1}sum_{d|D}d^{x-y}mu(frac Dd) ]

    然后我们发现后面的一堆项是积性函数,于是设:

    [f_i(n)=sum_{D|n}D^{i-1}sum_{d|D}d^{x-y}mu(frac Dd) ]

    假设(n=prod_{q=1}^{cnt}P_q^{k_q})(注意,(nle10^{18}),所以这里需要(Pollard-Rho)来对(n)质因数分解),那么根据积性函数的性质显然有:

    [f_i(n)=f(prod_{q=1}^{cnt}P_q^{k_q})=prod_{q=1}^{cnt}f(P_q^{k_q}) ]

    所以我们只需要考虑单个质数幂的函数得到:

    [f_i(P_q^{k_q})=sum_{D|P_q^{k_q}}D^{i-1}sum_{d|D}d^{x-y}mu(frac Dd) ]

    我们再先单独考虑后面的项,假设:(其中(D)是一个质数的幂)

    [g(D)=sum_{d|D}d^{x-y}mu(frac Dd) ]

    质数的幂,以及,(mu)?根据经验,当两者碰到一起,显然会发生神奇的事情。

    根据(mu)的定义,当(frac Ddge P_q^{2})时,(mu(frac Dd))就等于(0)了,所以我们只需考虑(frac Dd=1)(frac Dd=P_q)两种情况:

    [g(D)=D^{x-y}-(frac D{P_q})^{x-y} ]

    (注意,(D=1)的时候可能会出现点小问题——(frac D{P_q})挂了,这里先暂时不管,后面会解决的)

    然后我们把这个东西放回原式里得到:

    [f_i(P_q^{k_q})=sum_{D|P_q^{k_q}}D^{i-1}(D^{x-y}-(frac D{P_q})^{x-y}) ]

    考虑我们干脆令(D=P_q^t),然后枚举(t)。由于我们之前提到过的(D=1)时的小问题,我们单独处理(D=1)的情况(不要代入这个式子,代回原式就能发现显然此时值为(1)),并从(1)开始枚举(t),得到:

    [f_i(P_q^{k_q})=1+sum_{t=1}^{k_q}P_q^{t(i-1)}(P_q^{t(x-y)}-P_q^{(t-1)(x-y)}) ]

    看起来已经很完美了,但实际上即便只是快速幂的(log)还是会被卡。。。(当然如果你是常数之神就当我没说)

    所以我们继续往下,先提取出括号中的(P_q^{(t-1)(x-y)}=P_q^{t(x-y)-(x-y)})得到:

    [f_i(P_q^{k_q})=1+sum_{t=1}^{k_q}P_q^{t(i-1)+t(x-y)-(x-y)}(P_q^{x-y}-1) ]

    再进一步化简得到:

    [f_i(P_q^{k_q})=1+frac1{P_q^{x-y}}sum_{t=1}^{k_q}P_q^{t(i-1+x-y)}(P_q^{x-y}-1) ]

    然后我们就发现,只要先计算出(P_q^{i-1+x-y},P_q^{x-y}),并在枚举(t)同时累乘出(P_q^{t(i-1+x-y)}),就不需要再快速幂了。

    这样一来这题就真正做完了。

    最后再放一下最终的式子:

    [n^ysum_{i=0}^ya_in^{y-i+1}f_i(n) ]

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define X 1000000007
    #define LL long long
    #define RL Reg LL
    #define CL Con LL&
    using namespace std;
    LL n;int x,y;
    namespace PollardRho//质因数分解模板
    {
    	I LL gcd(CL x,CL y) {return y?gcd(y,x%y):x;}
    	I LL QM(CL x,CL y,CL P) {RL k=(1.0L*x*y)/P,t=x*y-k*P;t-=P;W(t<0) t+=P;return t;}
    	I LL QP(RL x,RL y,CL P) {RL t=1;W(y) y&1&&(t=QM(t,x,P)),x=QM(x,x,P),y>>=1;return t;}
    	namespace MR//MillerRabin质数测试
    	{
    		#define Pt 12
    		const int P[Pt]={2,3,5,7,11,13,17,19,61,2333,4567,24251};
    		I bool Check(CL x,CI p)
    		{
    			if(!(x%p)||QP(p%x,x-1,x)^1) return 0;
    			RL k=x-1,t;W(!(k&1)) {if((t=QP(p%x,k>>=1,x))^1&&t^(x-1)) return 0;if(t^1) return 1;}
    			return 1;
    		}
    		I bool IsP(CL x)
    		{
    			if(x==1) return 0;for(RI i=0;i^Pt;++i)
    				{if(x==P[i]) return 1;if(!Check(x,P[i])) return 0;}
    			return 1;
    		}
    	}
    	int cnt;struct data
    	{
    		LL p;int k;I data(CL a=0,CI b=0):p(a),k(b){}
    		I bool operator < (Con data& o) Con {return p<o.p;}
    		I bool operator == (Con data& o) Con {return p==o.p;}
    	}P[60];
    	I LL Work(CL x,CI y)
    	{
    		#define R(x) (1LL*rand()*rand()*rand()*rand()%(x)+1)
    		RI t=0,k=1;RL v0=R(x-1),v=v0,d,s=1;W(true)
    		{
    			if(v=(QM(v,v,x)+y)%x,s=QM(s,abs(v-v0),x),!s) return x;
    			if(++t==k) {if((d=gcd(s,x))^1) return d;v0=v,k<<=1;}
    		}
    	}
    	I void Resolve(RL x,CI w,RI t)
    	{
    		if(x==1) return;if(MR::IsP(x)) return (void)(P[++cnt]=data(x,w));
    		RL y;W((y=Work(x,t--))==x);RI u=0;W(!(x%y)) x/=y,++u;Resolve(x,w,t-1),Resolve(y,u*w,t-1);
    	}
    	I void Resolve(CL x)
    	{
    		cnt=0,srand(20050521),Resolve(x,1,302627441),sort(P+1,P+cnt+1);
    		RI i,tmp=cnt;for(i=2,cnt=1;i<=tmp;++i) P[i]==P[cnt]?(P[cnt].k+=P[i].k):(P[++cnt]=P[i]);//排序合并同一质数
    	}
    }using namespace PollardRho;
    I int QP(RI x,RI y) {RI t=1;y<0&&(y+=X-1);W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    class Bernoulli//伯努利数
    {
    	private:
    		#define SZ 3000
    		int B[SZ+5],C[SZ+5][SZ+5];
    	public:
    		I int operator [] (CI i) Con {return 1LL*QP(y+1,X-2)*C[y+1][i]%X*B[i]%X;}//求出第i项系数
    		I Bernoulli()//预处理
    		{
    			RI i,j;for(C[0][0]=i=1;i<=SZ+1;++i)//组合数
    				for(C[i][0]=j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%X;
    			for(B[0]=i=1;i<=SZ;++i)//伯努利数
    			{
    				for(j=0;j^i;++j) B[i]=(1LL*C[i+1][j]*B[j]+B[i])%X;
    				B[i]=1LL*(X-1)*QP(i+1,X-2)%X*B[i]%X;
    			}B[1]=X-B[1];//注意是正伯努利数
    		}
    }A;
    I int f(CI i,CI p,CI k)//求质数幂的f
    {
    	RI t,res=0,Pnow=1,Pbase=QP(p,i-1+x-y),P0=QP(p,x-y);//先计算好需要的幂
    	for(t=1;t<=k;++t) res=(1LL*(Pnow=1LL*Pnow*Pbase%X)*(P0-1)+res)%X;//累乘避免快速幂
    	return 1LL*res*QP(P0,X-2)%X+1;//返回答案
    }
    I int F(CI i)//求n的F
    {
    	RI q,res=1;for(q=1;q<=cnt;++q) res=1LL*f(i,P[q].p%X,P[q].k)*res%X;//枚举质因数把答案乘起来
    	return res;
    }
    int main()
    {
    	RI Tt,i,ans;scanf("%d",&Tt);W(Tt--)
    	{
    		scanf("%lld%d%d",&n,&x,&y),Resolve(n);
    		for(ans=i=0;i<=y;++i) ans=(1LL*A[i]*QP(n%X,y-i+1)%X*F(i)+ans)%X;//根据最终式子计算
    		printf("%d
    ",1LL*QP(n%X,y)*ans%X);
    	}return 0;
    }
    
  • 相关阅读:
    django with mysql (part-4)
    django with mysql (part-3)
    django with mysql (part-2)
    [LeetCode] 22. 括号生成(回溯/DP)
    [算法]求满足要求的进制(辗转相除(欧几里得算法),求最大公约数gcd)
    [算法]体积不小于V的情况下的最小价值(0-1背包)
    [LeetCode]96. 不同的二叉搜索树(DP,卡特兰数)
    [LeetCode]98. 验证二叉搜索树
    [LeetCode]21. 合并两个有序链表(递归)
    [LeetCode]538. 把二叉搜索树转换为累加树
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2627.html
Copyright © 2011-2022 走看看