G. 【美团杯2020】平行四边形
原根定义:
对于任意质数p都有一个原根g,满足gi%p!= gj%p,对于任意i!=j且i,j属于[1,p-1]都满足。
思路:
设p=n+1,对于每个点为(i,gi%p)
证明 假设存在平行四边形,即存在四个点(a,ga%p),(b,gb%p)(c,gc%p),(d,gd%p)为平行四边形,则
a-b=c-d,假设a>=b,c>=d;
ga%p-gb%p=gc%p-g ^d%p,
即为gb(g(a-b)-1)%p=gd(g(c-d)-1)%p,
因为a-b=c-d,所以g(a-b)-1==g(c-d)-1
两边同时消去得gb%p=gd%p;
因为g是p的原根,所以只能是b=d,同理a=c,即证明不存在平行四边形。
所以这种构造方法是正确的。
求原根一般就是暴力思想。
题目代码:
#include
#include
using namespace std;
const int MAX_N=1010;
bool vis[MAX_N];
long long pow_mod(long long a,long long n,long long m){
long long ans=1;
while(n){
if(n&1){
ans=(ans*a)%m;
}
a=(a*a)%m;
n>>=1;
}
return ans;
}
int main(void){
int T,i,j,n,g;
cin>>T;
while(T--){
scanf("%d",&n);
int p=n+1;
for(i=2;i<=n;i++){
int flag=1;
for(j=0;j<=p;j++)
vis[j]=false;
for(j=1;j<p;j++){
int x=pow_mod(i,j,p);
if(vis[x]){
flag=0;
break;
}
vis[x]=true;
}
if(flag){
g=i;
break;
}
}
for(i=1;i<=n;i++){
printf("%d %lld\n",i,pow_mod(g,i,p));
}
}
return 0;
}
求最小原根原根模板题
最小原根
求原根基本思路就是枚举,然后如果i是p的原根的话则对于(p-1)的所有质因数x都满足pow_mod(i,(p-1)/x,p)!=1.
而且一般最小原根挺小,而且一个数的质因子个数也不超过log个所以复杂度是klognlogn的复杂度,k就是最小原根,并且k挺小的,像这个题1e9跑的都很快。
#include
#include
using namespace std;
const int MAX_N=101000;
int prime[MAX_N],tot;
bool vis[MAX_N];
void init(int x){
for(int i=2;i<=x;i++){
if(!vis[i]){
vis[i]=true;
prime[++tot]=i;
}
for(int j=1;j<=tot&&i*prime[j]<=x;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
break;
}
}
}
}
long long pow_mod(long long a,long long n,long long m){
long long ans=1;
while(n){
if(n&1){
ans=(ans*a)%m;
}
a=(a*a)%m;
n>>=1;
}
return ans;
}
int sprime[MAX_N],cnt;//存放p的质因数
int main(void){
int p,i,j;
init(100000);
scanf("%d",&p);
int n=p-1;
for(i=1;i<=tot;i++){
if(n%prime[i]==0){
while(n%prime[i]==0){
n/=prime[i];
}
sprime[++cnt]=prime[i];
}
}
if(n!=1)
sprime[++cnt]=n;
int ans=-1;
for(i=2;i<p;i++){
int flag=1;
for(j=1;j<=cnt;j++){
if(pow_mod(i,(p-1)/sprime[j],p)==1){
flag=0;
break;
}
}
if(flag){
ans=i;
break;
}
}
printf("%d\n",ans);
return 0;
}