原根、FFT练习题

51nod 1135
题目描述
求小于1e9的素数p的原根

#include <bits/stdc++.h>
using namespace std;
int pi[40];
int tot=0;
int power(int x,int n,int p){
	int base=x;
	int ans=1;
	while(n){
		if(n&1){
			ans=1ll*ans*base%p;
		}
		n>>=1;
		base=1ll*base*base%p;
	}
	return ans;
}
int main(){
	int p;
	scanf("%d",&p);
	int x=p-1;
	for(int i=2;i*i<=x;i++){
		if(x%i==0){
			pi[++tot]=i;
			while(x%i==0)x/=i;
		}
	}
	if(x!=1)pi[++tot]=x;
	for(int i=2;i<p;i++){
		bool flag=true;
		for(int j=1;j<=tot;j++){
			if(power(i,(p-1)/pi[j],p)==1){
				flag=false;
				break;
			}
		}
		if(flag){
			printf("%d\n",i);
			break;
		}
	}
}

Loj 6156

题目描述

这是一个非常简单的问题。

wmq 如今开始学习乘法了!他为了训练自己的乘法计算能力,写出了 n n n 个整数,并且对每两个数 a , b a,b a,b 都求出了它们的乘积 a ⋅ b a\cdot b ab。现在他想知道,在求出的 n ( n − 1 ) 2 ​ \frac{n(n-1)}{2}​ 2n(n1)​​ 个乘积中,除以给定的质数 m m m 余数为 k ( 0 ≤ k &lt; m ) k(0\leq k&lt;m) k(0k<m) 的有多少个。

#include <bits/stdc++.h>
using namespace std;
typedef complex<double>E;
typedef long long ll;
const double pi=acos(-1.0);
int power(int x,int n,int p){
	int ans=1;
	int base=x;
	while(n){
		if(n&1)ans=1ll*ans*base%p;
		n>>=1;
		base=1ll*base*base%p;
	}
	return ans;
}
int n,m;
int p[40];
int tot;
int g;//原根
int to[60007];//i->g^j
int from[60007];//g^j->i
int cnt[60007];
E a[1<<18];
int R[1<<18];
ll ans[60007];
void fft(E a[],int op,int n){
	for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
	for(int i=1;i<n;i<<=1){
		E wn(cos(pi/i),op*sin(pi/i));
		for(int j=0;j<n;j+=(i<<1)){
			E w(1,0);
			for(int k=0;k<i;k++,w*=wn){
				E x=a[j+k],y=w*a[j+k+i];
				a[j+k]=x+y;a[j+k+i]=x-y;
			}
		}
	}
	if(op==-1)for(int i=0;i<n;i++)a[i]/=n;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		if(m==2)g=2;
		else {
			tot=0;
			int x=m-1;
			for(int i=2;i*i<=x;i++){
				if(x%i==0){
					p[++tot]=i;
					while(x%i==0){
						x/=i;
					}
				}
			}
			if(x!=1)p[++tot]=x;
			for(int i=2;i<m;i++){
				bool flag=true;
				for(int j=1;j<=tot;j++){
					if(power(i,(m-1)/p[j],m)==1){
						flag=false;
						break;
					}
				}
				if(flag){
					g=i;
					break;
				}
			}
		}
		int mul=1;
		for(int i=0;i<m-1;i++){
			to[mul]=i;
			from[i]=mul;
			mul=1ll*mul*g%m;
		}
		memset(cnt,0,sizeof(cnt));
		memset(ans,0,sizeof(ans));
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			cnt[x%m]++;
		}
		for(int i=1;i<m;i++){
			a[to[i]]=E(cnt[i],0);
		}
		int len=2*m-3;
		while(len!=(len&-len))len+=len&-len;
		for(int i=m-1;i<len;i++)a[i]=E(0,0);
		int L=31-__builtin_clz(len);
		for(int i=0;i<len;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
		fft(a,1,len);
		for(int i=0;i<len;i++){
			a[i]=a[i]*a[i];
		}
		fft(a,-1,len);
		for(int i=0;i<2*m-3;i++){
			ll tmp=(ll)(a[i].real()+0.5);
			ans[from[i%(m-1)]]+=tmp;
		}
		for(int i=1;i<m;i++){
			ans[1ll*i*i%m]-=cnt[i];
		}
		for(int i=1;i<m;i++){
			ans[i]/=2;
		}
		ll sum=1ll*n*(n-1)/2;
		for(int i=1;i<m;i++){
			sum-=ans[i];
		}
		ans[0]=sum;
		for(int i=0;i<m;i++){
			printf("%lld\n",ans[i]);
		}
	}
}

Hdu 5958
题目描述

吐槽
卡常毒瘤!fft p ∗ 2 p p*2p p2p 就GG了!不能把两个相同的部分一次卷积。。。一定要做一次 p ∗ p p*p pp 然后使用开竖式的技巧。。。而且不能用std的complex。。。
还有就是鬼畜的行末要有空格。。。
但是题还是不错的

#include <bits/stdc++.h>
using namespace std;
const double pi=acos(-1.0);
struct E {
	double r , i ;
	E () {}
	E ( double r , double i ) : r ( r ) , i ( i ) {}
	E operator + ( const E& p ) const {
		return E ( r + p.r , i + p.i ) ;
	}
	E operator - ( const E& p ) const {
		return E ( r - p.r , i - p.i ) ;
	}
	E operator * ( const E& p ) const {
		return E ( r * p.r - i * p.i , r * p.i + i * p.r ) ;
	}
} ;
int p;
int g;//原根
double a[100007],b[100007];
int to[100007];//i->g^j
int from[100007];//g^j->i
E A[1<<20],B[1<<20];
int R[1<<20];
void fft(E a[],int op,int n){
	for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
	for(int i=1;i<n;i<<=1){
		E wn(cos(pi/i),op*sin(pi/i));
		for(int j=0;j<n;j+=(i<<1)){
			E w(1,0);
			for(int k=0;k<i;k++,w=w*wn){
				E x=a[j+k],y=w*a[j+k+i];
				a[j+k]=x+y;a[j+k+i]=x-y;
			}
		}
	}
	if(op==-1)for(int i=0;i<n;i++){
		a[i].r/=n;
		a[i].i/=n;
	}
}
int main(){
	while(~scanf("%d",&p)){
		if(p==103)g=5;
		else g=2;
		int mul=1;
		for(int i=0;i<p-1;i++){
			to[mul]=i;
			from[i]=mul;
			mul=mul*g%p;
		}
		for(int i=0;i<p;i++){
			scanf("%lf",&a[i]);
			b[i]=a[0];
			if(i)b[0]+=a[i];
		}
		int len=2*p-3;
		while(len!=(len&-len))len+=len&-len;
		int L=31-__builtin_clz(len);
		for(int i=0;i<len;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
		for(int i=1;i<p;i++){
			A[p-2-to[i]]=E(a[i],0);
		}
		for(int i=p-1;i<len;i++)A[i]=E(0,0);
		mul=1;
		for(int i=0;i<p-1;i++){
			B[i]=E(pow(2,pow(sin(2*pi*mul/p),3)),0);
			mul=mul*g%p;
		}
		for(int i=p-1;i<len;i++)B[i]=E(0,0);
		fft(A,1,len);
		fft(B,1,len);
		for(int i=0;i<len;i++)A[i]=A[i]*B[i];
		fft(A,-1,len);
		for(int i=p-1;i<=2*p-4;i++)A[i].r+=A[i%(p-1)].r;
		for(int i=p-2;i<=2*p-4;i++){
			b[from[i-(p-2)]]+=A[i].r;
		}
		for(int i=0;i<p;i++)printf("%.3f ",b[i]);
		printf("\n");
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值