【美团杯2020】平行四边形(原根)

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<iostream>
#include<cstdio>
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<iostream>
#include<cstdio>
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值