HDOJ 6608 Fansblog

题目链接
Problem Description
Farmer John keeps a website called ‘FansBlog’ .Everyday , there are many people visited this blog.One day, he find the visits has reached P , which is a prime number.He thinks it is a interesting fact.And he remembers that the visits had reached another prime number.He try to find out the largest prime number Q ( Q < P ) ,and get the answer of Q! Module P.But he is too busy to find out the answer. So he ask you for help. ( Q! is the product of all positive integers less than or equal to n: n! = n * (n-1) * (n-2) * (n-3) *… * 3 * 2 * 1 . For example, 4! = 4 * 3 * 2 * 1 = 24 )

Input
First line contains an number T(1<=T<=10) indicating the number of testcases.
Then T line follows, each contains a positive prime number P (1e9≤p≤1e14)

Output
For each testcase, output an integer representing the factorial of Q modulo P.

Sample Input
1
1000000007

Sample Output
328400734

  • 题意
    给你一个素数p,q为小于它的最大素数,求q!%p

  • 思路
    显然题目可以分成两部分,1是求q,2是求q!%p。
    对1部分,最简单的做法就是从p-1开始向下枚举至第一个素数,对于一个足够大的素数n,不超过n的素数大约有n/ln n个即每ln n个数中大约有一个素数,故需要枚举的数量不超过100。
    对于2部分,直接求解最多优化到O( n l o g n \sqrt{n}logn n logn),加上1部分必然会超时。考虑威尔逊定理:对于素数p,有 ( p − 1 ) ! ≡ − 1 ( m o d p ) (p-1)! \equiv -1(mod p) (p1)!1(modp)故可以直接从(p-1)!开始推至q。

  • 代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
__int128 _a, _b, _m;
long long mul (long long a, long long b, long long m) {//防止爆ll
	_a = a, _b = b, _m = m;
	return (long long)(_a * _b % _m);
}
ll ex_gcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    else {
        ll r = ex_gcd(b, a % b, y, x);
        y -= x * (a / b);
        return r;
    }
}
ll inv(ll a, ll n) {
    ll x, y;
    ex_gcd(a, n, x, y);
    x = (x % n + n) % n;
    return x;
}
inline bool check (long long n) {
	for (long long i = 2; i * i <= n; i++) {
		if (n % i == 0) return true;
	}
	return false;
}
int main(){
    ll p,q,ans;
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%I64d",&p);
        ans=p-1;
        q=p-1;
        while(check(q)){
                ans=mul(inv(q,p),ans,p);//通过逆元倒推,此处会爆ll
            q--;
        }
        printf("%I64d\n",ans);
    }
}

  • upd
    还是看 n \sqrt{n} n 的素数判定方法不爽,用Miller-Robin判定改了下,时间减少太多了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
__int128 _a, _b, _m;
long long mul (long long a, long long b, long long m) {
	_a = a, _b = b, _m = m;
	return (long long)(_a * _b % _m);
}
ll ex_gcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    else {
        ll r = ex_gcd(b, a % b, y, x);
        y -= x * (a / b);
        return r;
    }
}
ll inv(ll a, ll n) {
    ll x, y;
    ex_gcd(a, n, x, y);
    x = (x % n + n) % n;
    return x;
}
int witness(ll a, ll n)//随机生成的a,来检测n的素性
{
	ll ans = 1;
	ll t = n - 1;//这里需要注意,你如果没有改变乘方的次数的话,最后的判断就是(ans == a) ? 0 : 1;
				 // 并且还要另外开辟空间来存储开始的a,比较麻烦,所以就这样了;
	ll x;
	while (t)
	{
		if (t & 1)
		{
			ans = mul(ans,a,n);
		}
		x = a;//从这里开始就是迭代乘法,验证二次验证定理
		a = mul(a,a,n);//这里就相当于 x*x % m = 1
		if (a == 1 && x != 1 && x != (n - 1))
		{
			return 1; // 这里需要注意,返回一的话就说明,追踪过程中,出现了不是素数的依据.
		}
		t >>= 1;
	}
	return (ans == 1) ? 0 : 1;
}
int MillerRobin(ll n, int s) // 一般s取50就可以避免所有的偶然性了.
{
	if (n == 2)
	{
		return 1;
	}
	if (n < 2 || !(n & 1))
	{
		return 0;
	}
	ll a;
	for (int i = 0; i < s; i++)
	{
		a = (long long int )rand() * (n - 2) / RAND_MAX + 1; //这样生成的随机数就是真正的随机数了
		if (witness(a, n))
		{
			return 0;
		}
	}
	return 1;
}
int main(){
    ll p,q,ans;
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%I64d",&p);
        ans=p-1;
        q=p-1;
        while(!MillerRobin(q,50)){
                ans=mul(inv(q,p),ans,p);
            q--;
        }
        printf("%I64d\n",ans);
    }
}
  • 吐槽
    这题会爆ll是真的毒瘤
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值