• BZOJ3560 DZY Loves Math V 数论 快速幂


    原文链接http://www.cnblogs.com/zhouzhendong/p/8111725.html

    UPD(2018-03-26):蒟蒻回来重新学数论了。更新了题解和代码。之前的怼到后面去了。


    题目传送门 - BZOJ3560


    题意概括

      给定$n$个正整数$a_1,a_2,a_3,...,a_n$,求

      $$Hugesum_{i_1|a_1}sum_{i_2|a_2}cdots sum_{i_n|a_n}varphi(i_1i_2i_3...i_n)$$

      答案对$10^9+7$取模。

      $1leq nleq 10^5,1leq a_ileq 10^7$

    题解

      考虑到$varphi$是积性函数,所以我们可以对于每一个质数分别考虑。

      对于每一个质数,考虑它有哪些情况,同一个质数的所有情况贡献加起来,然后不同质数的答案乘起来就OKla。

      考虑对一个质数的处理。

      先处理出每一个$a_i$含有该质因子几个(假设有$t_i$个)。保存好。

      我们显然不可能穷举所有情况。我们考虑采用不同的$a_i$分开贡献的方式。

      由于(p为质数)$f(p)=p-1,f(p^i)=f(p^{i-1})*p$,于是一开始的那个$f(p)=p-1$就特别令人不爽!!

      于是我们暂且假装$f(p)=p$。这样的话,数$a_i$的贡献就是$sum_{j=0}^{t_i}f(p^j)$。

      于是算出来的当前质数的总贡献就是$prod_{i=1}^{n}sum_{j=0}^{t_i}f(p^j)$。

      那个$sum_{j=0}^{k}f(p^j)$可以预处理。

      但是别忘了这个是个假贡献。我们假装了$f(p)=p$,事实上不是。

      我们考虑还原。

      该贡献可以分成两个部分:

        1.$prod_{j=1}^{n}i_j$中不含该质数因子,贡献为1。

        2.包含,贡献比标准多了$frac{1}{p-1}$。

      于是搞个逆元还原一下2的部分就可以得到正确答案了。

      具体实现大概我知道的有2种方式。

      设$m=max{a_i,iin[1,n]}$。

      一种是我之前抄的做法:先分解n个数,然后按照质因子排序分段处理。时间复杂度$O(n sqrt m +n log m)$。

      一种是我这次写的做法:先筛法把小于$sqrt m$的素数筛出来,然后对于每一个因子枚举n个数统计相关信息。对于大于$sqrt m$的质因数用map存下来。最后一个一个算质数贡献。复杂度相同。常数貌似变小了。(BZOJ上快了近4倍)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=1e5+5,M=1e7+5,mod=1e9+7;
    int prime[N],pcnt,ptot[N][30];
    bool f[M];
    map <int,int> hptot;
    void get_prime(){
    	pcnt=0;
    	memset(f,true,sizeof f);
    	f[0]=f[1]=0;
    	for (int i=2;i*i<M;i++){
    		if (!f[i])
    			continue;
    		prime[++pcnt]=i;
    		for (int j=1;j<=i;j++)
    			f[i*j]=0;
    	}
    }
    LL Pow(LL x,LL y){
    	if (!y)
    		return 1LL;
    	LL xx=Pow(x,y/2);
    	xx=xx*xx%mod;
    	if (y&1LL)
    		xx=xx*x%mod;
    	return xx;
    }
    LL Inv(LL x){
    	return Pow(x,mod-2);
    }
    int n,a[N],v[N],cntv=0;
    int main(){
    	get_prime();
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	memset(ptot,0,sizeof ptot);
    	hptot.clear();
    	for (int i=1;i<=pcnt;i++)
    		for (int j=1;j<=n;j++){
    			int k=0;
    			while (a[j]%prime[i]==0)
    				a[j]/=prime[i],k++;
    			ptot[i][k]++;
    		}
    	for (int i=1;i<=n;i++)
    		if (a[i]>1){
    			if (hptot[a[i]]==0)
    				v[++cntv]=a[i];
    			hptot[a[i]]++;
    		}
    	LL ans=1;
    	for (int i=1;i<=pcnt;i++){
    		LL phi=prime[i]+1,add=1;
    		for (int j=1;j<30;j++){
    			while (ptot[i][j]--)
    				add=add*phi%mod;
    			phi=(phi*prime[i]+1)%mod;
    		}
    		ans=ans*((add-1+mod)%mod*Inv(prime[i])%mod*(prime[i]-1)%mod+1)%mod;
    	}
    	for (int i=1;i<=cntv;i++){
    		LL phi=v[i]+1,add=1;
    		while (hptot[v[i]]--)
    			add=add*phi%mod;
    		ans=ans*((add-1+mod)%mod*Inv(v[i])%mod*(v[i]-1)%mod+1)%mod;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

      

     

      

     

    ——————Old——————

    题意概括

    给定n个正整数a1,a2,…,an,求

      

    的值(答案模10^9+7)。

    1<=n<=10^5,1<=ai<=10^7。


    题解

      本人是蒟蒻。

      可以看这位大佬的(给出链接)

    http://blog.csdn.net/popoqqq/article/details/42739963


    代码

    呵呵,调试的时候照着他的改,改的几乎一样……

    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long LL;
    const LL N=100005;
    LL mod=1e9+7;
    LL n,tot=0;
    struct Node{
    	LL p,t;
    }q[N*20];
    bool operator < (Node a,Node b){
    	return a.p<b.p;
    }
    void fj(LL v){
    	for (LL i=2;i*i<=v;i++)
    		if (v%i==0){
    			q[++tot].p=i;
    			q[tot].t=0;
    			while (v%i==0)
    				v/=i,q[tot].t++;
    		}
    	if (v>1)
    		q[++tot].p=v,q[tot].t=1;
    }
    LL Pow(LL x,LL y,LL mod){
    	if (y==0)
    		return 1LL;
    	LL xx=Pow(x,y/2,mod);
    	xx=xx*xx%mod;
    	if (y&1LL)
    		xx=xx*x%mod;
    	return xx;
    }
    LL Inv(LL x,LL mod){
    	return Pow(x,mod-2,mod);
    }
    LL calc(LL L,LL R){
    	LL sum[30];
    	LL p=q[L].p,res=1;
    	sum[0]=1;
    	for (int i=1;i<30;i++)
    		sum[i]=(sum[i-1]*p+1)%mod;
    	for (int i=L;i<=R;i++)
    		res=res*sum[q[i].t]%mod;
    	res=(res-1)*(p-1)%mod*Inv(p,mod)%mod+1;
    	return res%mod;
    }
    int main(){
    	scanf("%lld",&n);
    	for (int i=1,a;i<=n;i++){
    		scanf("%lld",&a);
    		fj(a);
    	}
    	sort(q+1,q+tot+1);
    	int last=0;
    	LL ans=1;
    	for (int i=1;i<=tot;i++)
    		if (i==tot||q[i].p!=q[i+1].p)
    			ans=ans*calc(last+1,i)%mod,last=i;
    	printf("%lld",(ans%mod+mod)%mod);
    	return 0;
    }
    

      

  • 相关阅读:
    NND,优酷效果实在太差了
    技术人员创业的短板
    【上架通知】天轰穿.NET4趣味编程视频教程VS2010轻松学习C#零基础
    最新课程信息 课堂风格视频教程,中规中矩的教学思路设计和插诨打科的讲解方式
    Visual Studio 2010 Ultimate 中文旗舰版——提供下载地址和KEY
    整整半个月了
    学云网五一特惠活动,很喜感的图
    学员就业>我心纠结
    委托的定义和使用入门天轰穿
    第八 讲 : 流程控制循环语句 【天轰穿.Net4趣味编程系列视频教程vs2010轻松学习C#】
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3560.html
Copyright © 2020-2023  润新知