在模p意义下,满足 ∀i∈[1,p−1]gi(modp) ∀ i ∈ [ 1 , p − 1 ] g i ( mod p ) 任意两两不相同的最小正整数g为p的原根。
对p-1进行质因数分解 p−1=∏ri=1prikii p − 1 = ∏ i = 1 r p r i i k i ,那么有
下面是一个利用上述性质暴力求原根的方法,由于一般我们认为原根较小(详情可以见下附表),所以就可以暴力枚举咯
#include
using namespace std;
typedef long long ll;
const int maxn=100010;
int cnt,tot,pri[maxn],vis[maxn],stk[maxn];
ll n,tmp;
void init()
{
for(int i=2;i<100000;i++)
{
if(!vis[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&(ll)i*pri[j]<100000;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]) break;
}
}
}
ll power(ll x,ll y,ll mod)
{
ll res=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1)
res=res*x%mod;
return res;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
init();
scanf("%lld",&n);
tmp=--n;
for(int i=1;i<=cnt;i++)
if(tmp%pri[i]==0)
{
stk[++tot]=pri[i];
while(tmp%pri[i]==0) tmp/=pri[i];
}
if(tmp^1) stk[++tot]=tmp;
for(int i=2,f;i<=n;i++)
{
f=1;
for(int j=1;j<=tot&&f;j++)
if(power(i,n/stk[j],n+1)==1)
f=0;
if(f){printf("%d\n",i);return 0;}
}
puts("Not found!");
return 0;
}
对于 gk≡x(x<p) g k ≡ x ( x < p ) ,我们称x的离散对数(亦称指标)为k,记做I(x)=k
它具有以下两个性质:
I(ab)≡I(a)+I(b)(modp−1) I ( a b ) ≡ I ( a ) + I ( b ) ( mod p − 1 )
I(ab)≡bI(a)(modp−1) I ( a b ) ≡ b I ( a ) ( mod p − 1 )
中文名叫快速数论变换,其实和FFT的用途是一样的,只不过由于FFT用到是复数,而且double在做了大量的实数运算之后精度损失较大,NTT就可以在模意义下,快速做这样的一个多项式乘法。好像NTT常数小一些
一般这个模数被认为是 r∗2k+1 r ∗ 2 k + 1 ,当然也有任意模数下的NTT ,但是我不会
看一道模板题多项式求逆
我们可以分治递归来做。我们记 xn=p x n = p , xceil(n/2)=p′ x c e i l ( n / 2 ) = p ′ ,记原多项式为A,模n意义下逆元为B,模n’意义下逆元为B’。
AB≡1(modp),AB′≡1(modp′) A B ≡ 1 ( mod p ) , A B ′ ≡ 1 ( mod p ′ )
由于p为p’的倍数,那么 AB≡1(modp′) A B ≡ 1 ( mod p ′ )
由同余的一些性质我们得到 B−B′≡0(modp′) B − B ′ ≡ 0 ( mod p ′ )
同时平方再展开 B2−2BB′+B′2≡0(modp) B 2 − 2 B B ′ + B ′ 2 ≡ 0 ( mod p )
同乘A得 B−2B′+AB′2≡0(modp) B − 2 B ′ + A B ′ 2 ≡ 0 ( mod p )
即 B≡2B′−AB′2(modp) B ≡ 2 B ′ − A B ′ 2 ( mod p )
当然如果p为奇数,那么2ceil(n/2)就会大于n,此时 p|p′2 p | p ′ 2 ,式子同样成立
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=2000010,mod=998244353,G=3;
int n,m,r[maxn],a[maxn],b[maxn],c[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int power(int x,int y)
{
int res=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1)
res=(ll)res*x%mod;
return res;
}
void ntt(int *a,int n,int f)
{
for(int i=0;iif(ifor(int i=1;i1)
{
int gn=power(G,(mod-1)/(i<<1));
for(int j=0;j1))
{
int x,y,g=1;
for(int k=0;kif(f==-1)
{
int inv=power(n,mod-2);reverse(a+1,a+n);
for(int i=0;ivoid work(int deg,int *a,int *b)
{
if(deg==1){b[0]=power(a[0],mod-2);return ;}
work((deg+1)>>1,a,b);
int len=0,fn=1;
while(fn<(deg<<1)) fn<<=1,len++;
for(int i=1;i>1]>>1)|((i&1)<<(len-1));
for(int i=0;ifor(int i=deg;i0;
ntt(c,fn,1);ntt(b,fn,1);
for(int i=0;i2,(ll)c[i]*b[i]%mod)*b[i]%mod;
ntt(b,fn,-1);
for(int i=deg;i0;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
scanf("%d",&n);
for(int i=0;i<=n;i++) scanf("%d",&a[i]);
work(n,a,b);
for(int i=0;iprintf("%d ",b[i]);
putchar('\n');
return 0;
}
r⋅2k+1 | r | k | g |
---|---|---|---|
3 | 1 | 1 | 2 |
5 | 1 | 2 | 2 |
17 | 1 | 4 | 3 |
97 | 3 | 5 | 5 |
193 | 3 | 6 | 5 |
257 | 1 | 8 | 3 |
7681 | 15 | 9 | 17 |
12289 | 3 | 12 | 11 |
40961 | 5 | 13 | 3 |
65537 | 1 | 16 | 3 |
786433 | 3 | 18 | 10 |
5767169 | 11 | 19 | 3 |
7340033 | 7 | 20 | 3 |
23068673 | 11 | 21 | 3 |
104857601 | 25 | 22 | 3 |
167772161 | 5 | 25 | 3 |
469762049 | 7 | 26 | 3 |
998244353 | 119 | 23 | 3 |
1004535809 | 479 | 21 | 3 |
2013265921 | 15 | 27 | 31 |
2281701377 | 17 | 27 | 3 |
3221225473 | 3 | 30 | 5 |
75161927681 | 35 | 31 | 3 |
77309411329 | 9 | 33 | 7 |
206158430209 | 3 | 36 | 22 |
2061584302081 | 15 | 37 | 7 |
2748779069441 | 5 | 39 | 3 |
6597069766657 | 3 | 41 | 5 |
39582418599937 | 9 | 42 | 5 |
79164837199873 | 9 | 43 | 5 |
263882790666241 | 15 | 44 | 7 |
1231453023109121 | 35 | 45 | 3 |
1337006139375617 | 19 | 46 | 3 |
3799912185593857 | 27 | 47 | 5 |
4222124650659841 | 15 | 48 | 19 |
7881299347898369 | 7 | 50 | 6 |
31525197391593473 | 7 | 52 | 3 |
180143985094819841 | 5 | 55 | 6 |
1945555039024054273 | 27 | 56 | 5 |
4179340454199820289 | 29 | 57 | 3 |