随笔 - 188  文章 - 0  评论 - 59  阅读 - 7690

扩展欧几里得和乘法逆元,欧拉函数 例题总结

1.洗牌解 2^m*x同余l(mod  (n+1))

因为m和x、n+1数据范围都比较大,所以肯定要化简2^m

不能用拓展欧几里得!a^b三a^b mod(fai(n+1)),因为2^m还乘了一个x,2^m、l不一定同余于(n+1)\

但是可以先把2^m mod(n+1)算出来(等价于2^m),最后解方程

这样是等价的(可以手摸一下)

/*
递推解决一部分:
int local,cnt
while(cnt--)
{
if(local&1)
{local=local+1  /2;
local=(total/2+local)
}
if(偶数)
{
local=local/2;
local=(total/2+local);
}
}循环吧 

x*2^m 同余last mod(n+1)
求一下fai(n+1)//
然后m mod(fai(n+1))减小运算次数
a=(2^m)  x =最终答案
b=(n+1)  y=无所谓
k=last  算吧 
 */
 ll n,m,l;
 ll fai(ll x)
 {
 	ll ans=x,start=sqrt(x+1);
 	_f(i,2,start)
 	{
 		if(x%i==0)
 		{
 			ans=ans-ans/i;
 			while(x%i==0)x/=i;
		 }
	}
	if(n!=1)ans=ans-ans/n;
		return ans;
 }
 ll x,y;
ll exgcd(ll a,ll b)
 {
 	if(b==0)
 	{
 		x=1,y=0;return a;
	 }
ll res=exgcd(b,a%b);
ll t=x;x=y;y=(ll)t-(ll)a/b*y;return res;
  } 
ll pw(ll t)//2^t
{
  	ll ans=1;ll jie=2;
  	while(t)
  	{
  		if(t&1)ans=ans*jie%(n+1);
  		jie=jie*jie%(n+1);
  		t>>=1;
	  }
	  return ans;
}
int main()
{
	//freopen("exam.txt","r",stdin);
n=re(),m=re(),l=re();
//ll fai_n=fai(n+1);
//	m=m%fai_n;//估计用到long long 
	ll a=1,b=(n+1);
	a=pw(m);
ll gcd=exgcd(a,b);//
	x=(x*(l/gcd)%(b/gcd)+b/gcd)%(b/gcd);
//	if(x==0)x+=b/gcd;
	chu("%lld",x); 
	return 0;
}









2.仪仗队
欧拉函数性质和计算
性质:【要求一个指数数字的fai】fai(p^k)=p^k-p^(k-1) 如果p是素数(因为要把能整除p^k的数表示成p*t)
【求一堆数字的fai】如果a mod b==0 fai(a*b)=b*fai(a) 把fai(a*b)拆开
                   !=0 fai(a*b)=(b-1)*fai(a)
代码见霹雳虎


3.求gcd(1-n,n)之和
分析问题,简化问题,提高效率的思想
开始 想到暴力枚举 60分
想:不如求出所有以x为gcd的数的个数,最后num[i]*i直接搞定,(找的时候把未知当作已知gcd(n,x)=now,同除now,gcd(n/now,x/now)=1,
不就是找n/now的fai吗,now一定是n的因子,所以找n的因子就行了)
90分
优化:找n的因子 1-sqrt(n),记得去掉重复的i*i

#include<stdio.h>
#include<math.h>
typedef long long ll;
ll euler(ll x)//欧拉函数
{
ll ans=x,tp=sqrt(x);
for(ll i=2;i<=tp;++i)
if(x%i==0)
{
ans=ans-ans/i;
while(x%i==0) x/=i;
}
if(x>1) ans=ans-ans/x;
return ans;
}
int main()
{
ll n;
scanf("%lld",&n);
ll ans=0,tp=sqrt(n);


for(ll i=1;i<=tp;++i)
if(n%i==0) ans+=i*euler(n/i)+n/i*euler(i);
if(tp*tp==n) ans-=tp*euler(tp);
printf("%lld\n",ans);
return 0;
}

 

4.莎拉公主的困惑

1--n!中与m!互质的数字的个数

n!:1*2*3*...*n,

任意gcd(x,n!)=1,必满足gcd(x,pi)=1,gcd(x,p(i+1))...=1

易得,if(gcd(a,b)=s),gcd(a+kb,b)=s

证明:存在x,y,gcd(x*s=a,y*s=b)=s,gcd(x,y)=1

gcd(a+kb=x*s+k*y*s,b=y*s)->gcd(s(x+ky),s(y));即证明:gcd(x+ky,y)=1;

假设i|(x+ky)  i|y  则i|ky  所以i|x,i=1;问题得证

  即在1--m!找到任意一个与m!互质的数,在n!/m!--n!中一定会有n!/m!个数与m!互质

可以看成gcd(x,m!)=1,那x*(1--n!/m!)个数,因为(m--n)中不可能再有与1--m互质但是还可以作为m!的因子的数

最终推出来的公式是ans=n!/m!*fai(m!)

【question1】fai(x!)=fai((x-1)!)*(x-1)或x(mod  r)

for(i:1--m)递推线性求解欧拉函数求解fai(m!)

【question2】n!/m!(mod r)求m!逆元,(线性求1--1e8逆元)

但是发现内存不够,还是费马小定理求(a^(p-2)*a同余于1(mod p))p为素数

最后解决一个问题:当r<=m<=n时,求逆元会直接ny[m]=0,结果怎么都是0了

可是手模一下发现是1,怎么回事?原来是n!/m!会同时有一个因数r,本来可以消掉但是我们单独取模就错了

可以在求jc的时候直接把r因子筛去,但是如果m<r<=n,相当于多删了,就在结尾特判,如果m<r<=n  ans=0

                  如果r>n  无所谓了,反正while里面的东西永远用不着

最后整合出标准输出答案:

ans=(jc[n]%r)*(ny[jc[m]]%r)(%r)*(fai[m!]%r)%r;

代码!(虽然是抄的)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<deque>
#include<queue>
#include<iomanip>
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define inf 0x7fffffff
#define chu printf
#define ll long long
using namespace std;
const int N=10000005;//这里本来我都开的ll 但是仔细看模数r是int 所以没有必要开ll,中间*1ll%r强转就行了
inline int re()
{
int h=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9')if(ch=='-')h=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*h;
}
int t,r,n,m;
int ispr[N];
int pri[N],fct[N],phi[N];//pri只到N,fct
inline void isprime()
{
int cnt=0;
ispr[0]=ispr[1]=1;//筛素数也要初始化!!!
_f(i,2,N-1)
{
if(!ispr[i])pri[++cnt]=i;
_f(j,1,cnt)
{
if(1ll*i*pri[j]>=N)break;
ispr[i*pri[j]]=1;
if(!i%pri[j])break;
}

}
}
inline ll qpow(int x,int p)//x^p,p<=1000,x is jc[m]%r (r is int)
{
ll res=1;
while(p)
{
if(p&1)res=1ll*res*x%r;
p>>=1;x=1ll*x*x%r;
}
return res;
}
int main()
{
// freopen("exam.txt","r",stdin);
isprime();
t=re(),r=re();
fct[1]=1;
_f(i,2,N-1)
{
int x=i;
while(x%r==0)x/=r;
fct[i]=1ll*fct[i-1]*x%r;
} phi[1]=1;
_f(i,2,N-1)
{
if(ispr[i])phi[i]=1ll*phi[i-1]*i%r;
else phi[i]=1ll*phi[i-1]*(i-1)%r;
}
while(t--)
{
n=re(),m=re();
int ans=1ll*((fct[n])*(qpow(fct[m],r-2))%r)*(phi[m])%r;

if(r>m&&r<=n)ans=0;


chu("%d\n",ans);
}
}


             
posted on   HZOI-曹蓉  阅读(101)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示