bzoj2186

欧拉函数+逆元

并没有什么想法

gcd有一个性质,gcd(i,j)=gcd(i+j,j),这个性质有什么用呢?我们发现phi(m!)找出了在m!以内所有和m互质的数,但是我们没有找到在n!范围内的数,但是根据刚才的性质,gcd(i,j)=gcd(i+j,j),那么gcd(i,m!)=1,gcd(i+m!,m!)=1,所以每个m!以内和m!互质的数都可以通过这个方法拓展,那么对于每个i,算上自己一共可以拓展n!/m!次,那么答案就是phi(m!)*n!/m!

根据欧拉函数的求法,phi(m!)=m!*π(pi-1)/pi,pi是m!的所有质因子,其实就是m以内所有质数,那么答案就是phi(m!)*n!/m!=π(pi-1)/pi*n!,这肯定是一个整数,然后就线性筛出pi,阶乘和逆元预处理出来就可以O(1)查询了。

线性求逆元貌似炸了,谁能帮忙看一下

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 10000010;
int T;
ll n, m, p;
ll ans[N], fac[N], inv[N];
int pri[500050];
bool mark[N];
void ini()
{
    ans[0] = ans[1] = fac[0] = fac[1] = 1;
    for(int i = 2; i <= 10000000; ++i)
    {
        fac[i] = fac[i - 1] * (ll)i % p;
        if(!mark[i]) pri[++pri[0]] = i;
        ans[i] = 1;
        for(int j = 1; j <= pri[0] && i * pri[j] <= 10000000; ++j)
        {
            mark[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;
        }
    }    
    inv[0] = inv[1] = 1;
    for(int i = 2; i <= 10000000 && i < p; ++i) inv[i] = (ll)(p - p / i * inv[p % i]) % p;
    for(int i = 2; i <= 10000000; ++i) 
    {
        if(!mark[i]) ans[i] = ans[i - 1] * (ll)(i - 1) % p * inv[i % p] % p;
        else ans[i] = ans[i - 1];
    }
}
int main()
{
    cin >> T >> p;
    ini();
    for(; T; --T)
    {
        scanf("%lld%lld", &n, &m);
        printf("%lld\n", ans[m] % p * fac[n] % p);
    }
    return 0;
}
View Code

exgcd求逆元

exgcd求逆元,我们求a关于p的逆元x,那么就是a*x=1(mod p),这不就是一个同余方程吗?那么可以转化为ax+py=1,求x的整数解,因为有逆元当且仅当gcd(a,p)==1,所以这个可以直接exgcd,求出x的最小正整数解,最小正整数解是(x%t+t)%t,t=p/gcd(a,p),因为gcd(a,p)==1,所以就是p了

具体可以看这里

http://www.cnblogs.com/19992147orz/p/7337301.html

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 10000010;
int T;
ll n, m, p;
ll ans[N], fac[N];
int mark[N], pri[N];
void exgcd(ll a, ll b, ll &x, ll &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return;
    }   
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
}
ll inv(ll n)
{
    ll x, y;
//  printf("n = %lld p = %lld\n", n, p);    
    exgcd(n, p, x, y);
    return (x % p + p) % p;
}
void ini()
{
    ans[0] = ans[1] = fac[0] = fac[1] = 1;
    for(int i = 2; i <= 10000000; ++i)
    {
        fac[i] = fac[i - 1] * (ll)i % p;
        if(!mark[i]) pri[++pri[0]] = i;
        ans[i] = 1;
        for(int j = 1; j <= pri[0] && i * pri[j] <= 10000000; ++j)
        {
            mark[i * pri[j]] = 1;
            if(i % pri[j] == 0) break;
        }
    }
    for(int i = 2; i <= 10000000; ++i) 
    {
        if(!mark[i]) ans[i] = ans[i - 1] * (ll)(i - 1) % p * inv(i) % p;
        else ans[i] = ans[i - 1];
    }
}
int main()
{
    cin >> T >> p;
    ini();
    for(; T; --T)
    {
        scanf("%lld%lld", &n, &m);
        printf("%lld\n", ans[m] % p * fac[n] % p);
    }
    return 0;
}
View Code

 发现了一些奇怪的问题,hzwer和popoqqq的程序样例都过不去。。。但是在bzoj上能过。。。线性求逆元是不是有bug啊。。。

posted @ 2017-09-14 10:15  19992147  阅读(213)  评论(0编辑  收藏  举报