• luogu 6月月赛 E 「数学」约数个数和


    题面在这里!

        第一眼感觉炒鸡水啊。。。只要把N质因数分解一下,因为k次约数相当于求k+2元一次方程的非负整数解,所以答案就是和每个质因子指数有关的一些组合数乘起来。

        但是要用pillard's rho啊。。。。

        (于是现学了一下,发现不会Miller Rabin。。。然后又先去学Miller Rabin 23333)

        Miller Rabin 的部分就不说了。。。随便找个博客肯定都讲的比我好多了2333

        Pillard's rho 的原理就是生日悖论,随机s个数之后两两作差并与n求gcd,当s不断增大的时候gcd>1的概率逐渐接近于100%

        问题是怎么随机。。。

        现在普遍在用的方法是 , 先随机一个c,一个x,然后令 y = ( x*x + c ) % n ,求一下gcd(|x-y| , n),如果>1那么返回,否则令x=y。

        但是这么做如果找不到的话会一直循环下去,所以要用一下floyd判环(我写的是倍增判环)。。。

        生成数列a[]的部分都是一样的,只不过是用 | a[now] - a[pre] | 和 n 取gcd 或 判重,其中 pre = 2^i,且i是满足pre<now的极大的i。。。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    #define T 20
    const int ha=998244353;
    
    ll zs[2333],phi,A,ans=1,N;
    int cnt=0;
    
    ll gcd(ll x,ll y){ return y?gcd(y,x%y):x;}
    
    inline ll add(ll x,ll y,const ll ha){ x+=y; return x>=ha?x-ha:x;}
    inline void ADD(ll &x,ll y,const ll ha){ x+=y; if(x>=ha) x-=ha;}
    
    inline ll mul(ll x,ll y,const ll ha){
    	ll an=0;
    	for(;y;y>>=1,ADD(x,x,ha)) if(y&1) ADD(an,x,ha);
    	return an;
    }
    
    inline ll ksm(ll x,ll y,const ll ha){
    	ll an=1;
    	for(;y;y>>=1,x=mul(x,x,ha)) if(y&1) an=mul(an,x,ha);
    	return an;
    }
    
    inline bool Mr(ll x){
    	if(x==2) return 1;
    	if(x==1||!(x&1)) return 0;
    	
    	int k=0;
    	ll now,pre,ci=x-1,b;
    	
    	for(;!(ci&1);ci>>=1,k++);
    	
    	for(int i=1;i<=T;i++){
    		b=rand()%(x-2)+2;
    		
    		now=ksm(b,ci,x),pre=now;
    		
    		for(int j=1;j<=k;j++,pre=now){
    			now=mul(now,now,x);
    			if(now==1&&pre!=1&&pre!=x-1) return 0;
    		}
    		
    		if(now!=1) return 0;
    	}
    	
    	return 1;
    }
    
    inline ll Get(const ll x,const ll M){ return add(mul(x,x,M),A,M);}
    
    inline ll Pr(ll n,ll c){    
        for(ll i=2,k=2,x=rand()%(n-1)+1,y=x,d;;i++){
        	x=add(mul(x,x,n),c,n);
        	
        	d=gcd(llabs(x-y),n);
        	if(d>1&&d<n) return d;
        	
        	if(x==y) return n;
        	
        	if(i==k) y=x,k<<=1;
    	}
    }
    
    void Find(ll x){
    	if(Mr(x)){ zs[++cnt]=x; return;}
    	
    	ll p=x;
    	while(p==x) p=Pr(p,rand()%(x-1)+1);
    	
    	Find(p),Find(x/p);
    }
    
    /*
    int main(){
    //	freopen("data.in","r",stdin);
    //	freopen("data.out","w",stdout);
        
        
    //	srand(time(0));
    	
    	scanf("%lld",&N);
    	Find(N),sort(zs+1,zs+cnt+1);
    	
    	for(int i=1;i<=cnt;i++) ans*=(ll)(zs[i]==zs[i-1]?zs[i]:(zs[i]-1));
    	
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    */
    
    
    ll K;
    int inv[67];
    
    int C(ll x,int y){
        int an=inv[y];
        for(ll i=x-y+1;i<=x;i++) an=an*(ll)(i%ha)%ha;
        return an;
    }
    
    int main(){
        srand(time(0)+19260817);
        
        inv[1]=inv[0]=1;
        for(int i=2;i<=62;i++) inv[i]=-inv[ha%i]*(ll)(ha/i)%ha+ha;
        for(int i=2;i<=62;i++) inv[i]=inv[i]*(ll)inv[i-1]%ha;
        
        cin>>N>>K;
        
        if(N<=1e9){
            for(int i=2;i*(ll)i<=N;i++) while(!(N%i)) N/=i,zs[++cnt]=i;
            if(N!=1) zs[++cnt]=N;
        }
        else Find(N),sort(zs+1,zs+cnt+1);
        
        
        zs[cnt+1]=0;
        for(int i=1,tot=0;i<=cnt;i++){
            tot++;
            if(zs[i]!=zs[i+1]){
                ans=ans*(ll)C((ll)tot+K+1ll,tot)%ha;
                tot=0;
            }
        }
        
        cout<<ans<<endl;
        return 0;
    }
    

      

  • 相关阅读:
    直接插入排序
    直接选择排序
    冒泡排序
    归并排序
    进程调度
    进程与线程
    c语言struct和c++struct的区别
    二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    6-11 先序输出叶结点
  • 原文地址:https://www.cnblogs.com/JYYHH/p/9215079.html
Copyright © 2020-2023  润新知