hdu4704 && FZU1759 && HYSBZ3884 &&POJ3090 欧拉函数 || 欧拉降幂 ||费马小定理

hdu 4704

 

 

思路: 题目本身很简单,就是讲N划分为K个数的和有几种划分(1.....K)对应每一个K有 C(N-1,K-1)中划分法,一共就是 2^(N-1)。

但是发现N最多为 10^100000 哪怕快速幂也会爆.

 

方法1:  这也是我自己的一个做法吧,因为取模了,可以用欧拉降幂公式.

PS : 1e9+7 是大素数,所以他的欧拉函数值为1e9+6(根本不需要跑欧拉,可以直接得出),相比N的值已经小了很多了.跑一次快速幂就好. 而且并不要求a和n互质!!!!!

复杂度 O(log(1e9+6)).当 b<φ(n)时,b很小,直接快速幂即可,

 

简单介绍一些欧拉函数:

φ(n)定义为不超过n且与n互质的正整数的个数. 如:φ(3) = φ(6)=2等。

1. 欧拉函数的性质

p是n的质因子,那么有: φ(i*n*p)= φ(i*n)*p

推论: 当n为奇数时,φ(2*n) = φ(n)

 2. 欧拉函数的三种求法 : 

(1) 枚举 不超过n的数,暴力判断   复杂度 nlogn
(2) 利用欧拉求值公式:  复杂度 sqrt(n)

  (3)  欧拉筛  O(n) 筛出1~n φ(n) ,O(1)查询.

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
char s[maxn];
/*
ll phi(ll n)
{
	ll res=n,a=n;
	for(int i=2;i*i<=n;i++)
	{
		if(a%i==0)
		{
			res-=res/i;
			while(a%i==0) a/=i;
		}
	}
	if(a>1)
	res-=res/a;
	return res;
}*/

ll qmod(ll x,ll y)
{
	ll res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}

int main()
{
	//ll phim=phi(mod);
	ll phim=1000000006;
	//printf("%lld\n",phim); 
	while(~scanf("%s",s))
	{
		ll res=0;
		int len=strlen(s),i;
		for(int i=0;i<len;i++)
		{
			res=res*10+s[i]-'0';
			if(res>mod)
			break;
		}
		if(i==len)
		printf("%lld\n",qmod(2,res-1));
		else
		{
			res=0;
			for(int i=0;i<len;i++)
			{
				res=res*10+s[i]-'0';
				res%=phim;
			}
			printf("%lld\n",qmod(2,res-1+phim));
		}
	}
	return 0;
}

 

 

 

 

 

网上还有大牛用费马小定理做的,我也来学习一发. 

费马小定理:

 

特别的,当p为素数时,x无法被p整除,φ(p)=p-1,于是便有费马小定理Xp-1≡1(mod p)

在p是素数时,对任意正整数x都有Xp≡X(mod p)

 

那么对于这个题目,我们可以将2 ^(n -1 ) 的(n - 1 )拆成 k*(p-1)+t  ,对于 2^(k*(p-1)) 根据费马小定理得到的答案为1,相乘对结果无影响,所以只需计算 2^t 即可. 这样也就保证了把 指数控制在了p范围内,是一个非常强的想法.

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
char str[maxn];

ll qmod(ll x,ll y)
{
	ll res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
ll xiaofeima(ll m)
{
	int len = strlen(str);
	ll res = 0;
	for(int i = 0; i < len ; i++)
	{
		res = res * 10 + str[i] - '0';
		res %= m;
	}
	res = (res + m - 1) % m;//这里我是先拆的n,所以最后结果要-1,但是要取模. 
	ll ans = qmod(2,res);
	return ans;
}
int main(){

	while(~scanf("%s",str))
	{
		ll ans = xiaofeima(mod - 1);
		printf("%lld\n",ans);
	}
	return 0;
}




FZU 1759

 

Given A,B,C, You should quickly calculate the result of A^B mod C.

(1<=A,C<=1000000000,1<=B<=10^1000000).

这个题和上面那个就一样了,裸的欧拉降幂.因为这里C不一定是质数,所以无法使用费马小定理,而欧拉降幂公式对A 和C 是否互质未作要求

因为这里就求一次,所以直接用O(sqrt(n))的欧拉求值函数公式求一次即可.

 

#include<iostream>
#include<cstdio>
#include<cstring> 
using namespace std;

typedef long long ll;
const int maxn=1e6+10;
ll a,c;
char b[maxn];
ll phi(ll n)
{
	ll res=n,tmp=n;
	for(int i=2;i*i<=n;i++)
	{
		if(tmp%i==0)
		{
			res-=res/i;
			while(tmp%i==0)
			tmp/=i;
		}
	}
	if(tmp>1)
	res-=res/tmp;
	return res;
 } 
ll qmod(ll x,ll y)
{
	x%=c;
	ll res=1;
	while(y)
	{
		if(y&1)
		res=res*x%c;
		x=x*x%c;
		y>>=1;
	}
	return res;
}

int main()
{
	while(~scanf("%lld %s %lld",&a,b,&c))
	{
		ll res=0;
		ll phic=phi(c);
		int len=strlen(b),i;
		for(i=0;i<len;i++)
		{
			res=res*10+b[i]-'0';
			if(res>phic)
			break;
		}
		if(i==len)
		{
			printf("%lld\n",qmod(a,res));
		}
		else
		{
			res=0;
			for(i=0;i<len;i++)
			{
				res=(res*10+b[i]-'0')%phic;
			}
			printf("%lld\n",qmod(a,res+phic));
		}
	}
	return 0;
}

 

 

 

HYSBZ - 3884

图片地址

 

 

 

 

根据上面这个式子可以发现我们每进行一次欧拉降幂,就可以减少一个2,而且mod的p,将一次幂变成了φ(p).那么就肯定有一个时刻φ(p) 为1 ,那么 任何一个数%1=0,则f(p)=1.这个过程可以递归来做即可.

 

 

PS  :这个题目的话你其实直接对于每个p去求欧拉函数,比全部筛出来要快...  复杂度为 O(T*sqrt(n)*logn)。

 欧拉筛的话复杂度 O(p+T*logp)。可能因为p太小?

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;
const int maxn=1e7+10;

vector<int>prime;
int phi[maxn];
//欧拉筛模板 
void init()
{
	phi[1]=1;
	for(int i=2;i<maxn;i++)
	{
		if(phi[i]==0)
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&prime[j]*i<maxn;j++)
		{
			if(i%prime[j]==0)//关键 
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else
				//phi[i*prime[j]]=phi[i]*phi[prime[j]];
				phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}

ll qmod(ll x,ll y,ll mod)
{
	ll res=1;
	while(y)
	{
		if(y&1)
		res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}


ll solve(ll p)
{
	if(p==1ll) return 0;
	return qmod(2,(ll)solve((ll)phi[p])+(ll)phi[p],p);
}

int main()
{
	
	init();
	int t;
	scanf("%d",&t);
	while(t--)
	{
		ll p;
		scanf("%lld",&p); 
		printf("%lld\n",solve(p));
	}
	return 0;
}

 

 

 

 

 

 

#include <map> 
#include <cstdio>
using namespace std;
map<int, int> f;
int pow(int x, int k, int p)
{
    int ret = 1;
    while(k)
    {
        if(k & 1)
            ret = (long long)ret * x % p;
        x = (long long)x * x % p;
        k >>= 1;
    }
    return ret;
}
int phi(int x)
{
    int ret = x;
    for(int i = 2; i * i <= x; ++i)
        if(x % i == 0)
        {
            ret -= ret / i;
            while(x % i == 0)
                x /= i;
        }
    if(x > 1)
        ret -= ret / x;
    return ret;
}
int F(int x)
{
    if(f.count(x))
        return f[x];
    int p = phi(x);
    return f[x] = pow(2, F(p) + p, x);
}
int main()
{
    int t, n;
    scanf("%d", &t);
    f[1] = 0;
    while(t--)
    {
        scanf("%d", &n);
        printf("%d\n", F(n));
    }
    return 0;
}

 

POJ 3090

 

 

 

题意:

 

有一个n*n的二维格点,问在原点(0,0)处能看到多少个格点?(n<=1000,1000组数据)

 

 

思路:

 

我们可以发现什么样的点才是能看到的呢?对于一个点(x,y) ,他可以在原点被看到当且仅当 gcd(x,y) == 1

     其次我们可以发现,所能看到的点是关于对角线对称的,所以我们可以只算一个上三角然后对称一下即可. 

我们可以固定纵坐标,寻找横坐标,那么横坐标的所有可能就是 纵坐标 y的φ(y)。然后y从2~n 求和即可.

PS : y轴和x轴还有对角线的在我们计算过程中没算进去,要加上

一个欧拉筛即可.

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define inf 0x3f3f3f3f
using namespace std;

typedef long long ll;
const int maxn=1e3+10;

vector<int>prime;
int phi[maxn];
void init()
{
	phi[1]=1;
	for(int i=2;i<maxn;i++)
	{
		if(phi[i]==0)
		{
			prime.push_back(i);
			phi[i]=i-1;
		}
		for(int j=0;j<prime.size()&&prime[j]*i<maxn;j++)
		{
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else
				phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
}
int main()
{
	int t;
	cin>>t;
	init();
	int ca=1;
	int n; 
	while(t--)
	{
		scanf("%d",&n);
		ll ans=3;
		for(int i=2;i<=n;i++)
		ans+=2*phi[i];
		printf("%d %d %lld\n",ca++,n,ans);
	 } 
	return 0;
}

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值