T661179 8.27考试t1
题目链接:
https://www.luogu.com.cn/problem/T661179
题目描述
给你一个数p,缩减剩余系是指由[1,p]之间的与p互质的整数组成的集合。现在给你一个数x(x在p的缩减剩余集中),求在模p意义下x的幂集有多少个不同的值,(形如1,\(x\),\(x^2\),\(x^3\),...的数叫做x的幂集)
暴力求解?
考试的时候首先想到的是暴力枚举x的次幂,一直到第二次出现1就说明进入了循坏(第一次即x^0=1),代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t,p,x;
cin>>t;
for(int i=1;i<=t;i++){
cin>>p>>x;
int ans=1,m;
m=x;//每上升一次幂就是在前一次幂的基础上在乘一个x,考虑后续x的值会变所以先记录下来
x=x%p;
if(x==1){
cout<<ans<<endl;
continue;
}
ans++;//i等于1时特殊处理
while(1){
x=(x*m)%p;//边乘边取余,不然会爆(我是不会告诉你我因为这个原因考场爆零的
if(x==1) break;
ans++;
}
cout<<ans<<endl;
}
return 0;
}
由于50%的数据p<=1e4,所以这段代码能得50分;
正解
如何优化?再次读题容易发现x属于p的缩减剩余集这个条件是没有在暴力做法中用上的;重新整理暴力做法的思路,其实就是求一个除0外最小的n,使得:
\(x^n\equiv1\bmod p\)
由于x和p互质,很容易想到欧拉定理(别问我考试咋没想到):
\(a^{\varphi(P)}\equiv1\bmod p (gcd(a,p)=1)\)
根据欧拉定理可知n=\(\varphi(P)\)时一定循环到1,所以最小的n一定是\(\varphi(P)\)或是其因子,依次枚举即可
正解代码如下:
#include<bits/stdc++.h>
#define ll long long//貌似其实可以不开long long
using namespace std;
ll t,p,x,y[10005],tot;
ll oula(ll n){//求欧拉函数
ll s=n;
for(int i=2;i*i<=n;i++){
if(n%i==0){
s-=s/i;
while(n%i==0) n/=i;
}
}
if(n>1) s-=s/n;
return s;
}
ll quai(ll x,ll y,ll mod){//快速幂
ll m=1;
ll n=x;
while(y>0){
if(y&1){
m=m*n;
m=m%mod;
}
n=n*n;
n=n%mod;
y>>=1;
}
return m;
}
void qiuyinzi(int x){求因子
for(int i=1;i*i<=x;i++){
if(x%i==0){
y[tot++]=i;
y[tot++]=x/i;//i是因子所以x/i一定也是因子
}
}
sort(y,y+tot);//由于不是从小到大加入的,所以排一下序
}
int main(){
cin>>t;
for(int i=1;i<=t;i++){
tot=1;
cin>>p>>x;
ll n,a;
a=oula(p);
qiuyinzi(a);
for(int j=1;j<=tot;j++){
int n=quai(x,y[j],p);
if(n==1){
cout<<y[j]<<endl;
break;
}
}
}
return 0;
}