原根求解算法 && NTT算法

原根求解算法:

获取一个数\(N\)的原根\(root\)的算法

#include<bits/stdc++.h>
#define ll long long
#define IL inline
#define RG register
using namespace std;

ll prm[1000],tot,N,root;

ll Power(ll bs,ll js,ll MOD){
    ll S = 1,T = bs;
    while(js){
    	if(js&1)S = S*T%MOD;
		T = T*T%MOD; 
		js >>= 1; 
	} return S;
}

IL ll GetRoot(RG ll n){
	RG ll tmp = n - 1 , tot = 0;
	for(RG ll i = 2; i <= sqrt(tmp); i ++){
		if(tmp%i==0){
			prm[++tot] = i;
			while(tmp%i==0)tmp /= i;
		}
	}
	if(tmp != 1)prm[++tot] = tmp;            //质因数分解
	for(RG ll g = 2; g <= n-1; g ++){
		bool flag = 1;
		for(RG int i = 1; i <= tot; i ++){     //检测是否符合条件
			if(Power(g,(n-1)/prm[i],n) == 1)
			    { flag = 0; break; } 
		}
		if(flag)return g;
	}return 0;                        //无解 
}

int main(){
	cin >> N;
	root = GetRoot(N);
	cout<<root<<endl;
	return 0;
}

快速数论变换算法:

计算多项式\(f_1*f_2\)在模\(P\) (\(P\)为质数) 意义下的卷积。
讲真的,只要把\(FFT\)的单位复数根换成原根就行了。
注意要提前用上面的算法把模数的原根算出来。

#define mod 998244353     //使用NTT需要保证模数mod 为质数
const ll pr = 3;  
 //3是998244353的原根,在比赛中请用上面那个算法提前算出....

ll f1[_],f2[_],U,V;
ll wn[50],R[_],N,M,n,m,l,ans[_];

IL ll Power(RG ll bs,RG ll js){
    RG ll S = 1 , T = bs;
    while(js){if(js&1)S=S*T%mod; T=T*T%mod; js>>=1;}
    return S;
}

IL void GetWn(){
    //需要计算floor(log n)个原根
    for(RG int i = 0; i <= 25; i ++){
        RG ll tt = 1<<i;
        wn[i] = Power(pr,(mod-1)/tt);
    }return;
}

IL void NTT(RG ll P[],RG int opt){
    for(RG int i = 0; i < n; i ++)
        if(i < R[i]) swap(P[R[i]],P[i]);
    for(RG int i = 1,id = 0; i < n; i<<=1){
    	id ++;
    	for(RG int j = 0,p = i<<1; j < n; j += p){
    		RG ll w = 1;
    		for(RG int k = 0; k < i; k ++,w = w*wn[id]%mod){
    			U = P[j+k]; V = w*P[j+k+i];
    			P[j+k] = (U+V)%mod;  P[j+k+i] = ((U-V)%mod+mod)%mod;
			}
		}
	}
	if(opt == -1){
        //caution:反转时是从1开始 for !!!!!
		for(RG int i = 1; i < n/2; i ++)swap(P[i],P[n-i]);  
		RG ll inv = Power(n,mod-2);
		for(RG int i = 0; i < n; i ++)P[i] = P[i]%mod*inv%mod;
	}return;
}

int main(){
    //读入数据:
    cin >> N >> M;
    for(RG int i = 0; i <= N; i ++)cin >> f1[i];
    for(RG int i = 0; i <= M; i ++)cin >> f2[i];

    //NTT计算:
    m = N+M; l = 0;
    for(n = 1; n <= m; n<<=1) ++ l;
    for(RG int i = 0; i < n; i ++)
        R[i] = (R[i>>1]>>1) | ((i&1)<<(l-1));
    GetWn();
    NTT(f1,1);  NTT(f2,1);
    for(RG int i = 0; i < n; i ++)f1[i] = f1[i]*f2[i]%mod;
    NTT(f1,-1);
    
    //转移答案:
    for(RG int i = 0; i <= m; i ++)ans[i] = f1[i];
    for(RG int i = 0; i <= m; i ++)cout<<ans[i]<<" ";
    return 0;
}

原文地址:https://www.cnblogs.com/Guess2/p/8362004.html