逆元 阶乘逆元 数论 HDU 5651 xiaoxin juju needs help

50 篇文章 0 订阅
26 篇文章 0 订阅

逆元用于用于对除法运算取模的运算

\frac{a}{b} mod $ $ m = a \cdot inv(b) mod $ $ m

inv(x) 表示 x逆元

inv(x) \cdot x \equiv 1 (mod $ $m)

求解逆元

方法一:拓展欧几里得求逆元(求单个数的逆元)

已知 \\a\cdot x + b\cdot y = 1

同时取模bax \equiv 1(mod $ $b)

可见xa的逆元

所以inv(x) = ex\_gcd(a, b, x, y)中的x

int ex_gcd(int a, int b, int &x, int &y){
    if (b == 0){
        x = 1;
        y = 0;
        return a;
    }
    int tmp = ex_gcd(b, a % b, y, x);
    y -= a/b*x;
    return tmp;
}

int inv(int a, int mod){ //a对于mod的逆元
    int x, y;
    ex_gcd(a, mod, x, y);
    return x;
}

方法二:费马小定理(求单个数的逆元)

\\x^{p-1} \equiv 1 (mod $ $ p) \\x\cdot x ^{p-2} \equiv 1(mod $ $ p)

可见x^{p-2}x的逆元

int fp(int x, int p){
    if (p == 1) return 1 % mod;
    int tmp = fp(x, p / 2);
    if (p & 1) return tmp * tmp % * x % mod;
    return tmp * tmp % mod;
}

int inv(int a, int mod){
    return fp(a, mod - 2);
}

方法三:线性求逆元(求一段区间内的所有逆元)

inv(x) = (m -\frac{m}{i})inv(m\%i) \%m

假设 t = \frac{m}{i} , k = m \% i
\\t * i + k \equiv 0 ( mod m ) \\- t * i \equiv k ( mod m ) \\- t * 1 / k \equiv 1 / i ( mod m )\\-t*inv(k) \equiv inv(i) (mod m) 
再替换 t,k 
inv [ i ] = ( m - m / i ) * inv [ m \% i ] \% m

inv[1] = 1;  
for(int i = 2; i <= n ; i++)  
    inv[i] = ( m - m / i ) * inv[ m % i ] % m;


在求解组合数的时候,分子通常会出现某个数的阶乘,下面讲解阶乘逆元

方法一:正推(同时用到\large inv(x) inv(x!)

inv(x!)记为inv\_fac(x)

inv\_fac(i) = inv\_fac(i-1)* inv(i)

inv[1]=inv_fac[i]=1;
for(int i=2;i<N;i++){  
    inv[i]=(mod-mod/i)*inv[mod%i]%mod;  //求inv[i]
    inv_fac[i]=inv[i]*inv_fac[i-1]%mod;
}

方法二:逆推(只用到\large inv(x!)

先用费马小定理或者拓展欧几里得先求出inv(n!)

\\\because inv(x!) = inv((x-1)!)*inv(x) \\\therefore inv(x!)*x= inv((x-1)!)

    inv_fac[N] = fp(fac[N], mod - 2ll);
    for (int i = 999; i >= 1; i--){
        inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;
    }

例题HDU5651

xiaoxin juju needs help

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2981    Accepted Submission(s): 993


 

Problem Description

As we all known, xiaoxin is a brilliant coder. He knew **palindromic** strings when he was only a six grade student at elementry school.

This summer he was working at Tencent as an intern. One day his leader came to ask xiaoxin for help. His leader gave him a string and he wanted xiaoxin to generate palindromic strings for him. Once xiaoxin generates a different palindromic string, his leader will give him a watermelon candy. The problem is how many candies xiaoxin's leader needs to buy?

 

 

Input

This problem has multi test cases. First line contains a single integer T(T≤20) which represents the number of test cases.
For each test case, there is a single line containing a string S(1≤length(S)≤1,000).

 

 

Output

For each test case, print an integer which is the number of watermelon candies xiaoxin's leader needs to buy after mod 1,000,000,007.

 

 

Sample Input

3 aa aabb a

 

 

Sample Output

1 2 1

 

 

Source

BestCoder Round #77 (div.2)

 

 

Recommend

wange2014

code

/*
 *逆元在乘法中的运用
 */
#include <bits/stdc++.h>
#define FOR(I,S,T) for(int I=(S);I<=(T);I++)
#define pb push_back
#define mp make_pair
#define eb emplace_back

using namespace std;
typedef long long ll;
const int maxn = 1e4+2;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;

string s;
ll inv_fac[maxn];
ll fac[maxn];
ll fp(ll x, ll p){
    if (p == 0) return 1 % mod;
    ll t = fp(x, p / 2) % mod;
    if (p & 1) return t * t % mod * x %mod;
    return t * t % mod;
}
int cnt[maxn];
vector <int> tmp;
int main()
{
    int T;cin >> T;
    fac[0] = 1;
    FOR(i,1,1000) fac[i] = fac[i - 1] * i % mod;
    inv_fac[1000] = fp(fac[1000], mod - 2ll);
    for (int i = 999; i >= 1; i--){
        inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;
    }
    inv_fac[0] = inv_fac[1];
    int f = 0;
    while (T--){
        tmp.clear();
        cin >> s;
        sort(s.begin(), s.end());
        cnt[0]= 1;
        f = 0;
        for (int i = 1; i < s.size(); i++){
            if (s[i] == s[i-1] ) cnt[i]= cnt[i - 1]+1;
            else {
                cnt[i] = 1;
                if (cnt[i -1] & 1) f++;
                if (cnt[i-1] & 1) tmp.eb(cnt[i-1]-1);
                else tmp.eb(cnt[i-1]);
            }
        }
        if (s.size() > 1){
            if (cnt[s.size() - 1] & 1) f++,tmp.eb(cnt[s.size() - 1] -1);
            else tmp.eb(cnt[s.size() - 1]);
        }
        if (f > 1){
            puts("0");
            continue;
        }
        ll ans = fac[s.size() / 2];
        for (int i = 0; i < tmp.size(); i++){
            ans = ans * inv_fac[tmp[i] / 2] % mod;
        }
        cout << ans << endl;
    }

    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值