HDU 3049 Data Processing(a/b mod c, 逆元)

本文探讨了模运算中的常见问题及解决方法,特别是针对形如 a/n mod n 的复杂表达式的计算技巧,包括直接计算、利用逆元求解等,并提供了具体的代码实现。

题目:LINK

题目求 P = (2^(n1) + 2^(n2) + ...+ 2^(nk))/n  (mod n)
    对于分子部分我们可以打表求得(ps:用快速幂多次计算会TLE), 问题成了a/n mod n, 我们可以用下面解法求得。
1), a/b mod c ==>  (a mod bc) / b 对于所有的情况都适用,要注意的问题就是 (b*c)*(b*c) 会不会溢出,这儿的数据范围不会溢出.
2), 求利用逆元求解。逆元存在的条件是gcd(b, c) == 1
    1‘ 如果c 是质数,可以利用费马小定理求得,b ^(-1) = b^(c-2) .
    2’ 一般情况下可以利用扩展欧几里得求得, 设b的逆元是x , bx= 1 mod c, ==>  bx + cy = 1 求解(x, y) 中x就是逆元,(gcd(b, c) != 1)  没有逆元)

1)code: 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define MOD 1000003
#define N 41111
LL n, num[N], b[N];
LL sol()
{
    LL ret = 0, mod = MOD*n;
    b[0] = 1;
    for(int i = 1; i<=40001; i++) {
        b[i] = 2*b[i-1];
        if(b[i]>= mod) b[i] -= mod;
    }
    for(int i = 1; i<= n; i++) {
        LL tmp = b[num[i]];
        ret += tmp; ret %= mod;
    }
    return ret / n;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int t, time = 0;
    scanf("%d", &t);
    while( t-- ) {
        scanf("%I64d", &n);
        for(LL i = 1; i <= n; i++) {
            scanf("%I64d", &num[i]);
        }
        LL ans = sol();
        printf("Case %d:", ++time);
        printf("%I64d\n", ans);
    }
	return 0;
}
2) 1' code :

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define MOD 1000003
#define N 41111
LL n, num[N], b[N];

LL powmod(LL x, LL y, LL mod)
{
    LL ret = 1;
    while(y) {
        if(y&1) ret = ret * x, ret %= mod;
        y >>= 1;
        x *= x;  x %= mod;
    }
    return ret;
}
LL sol()
{
    LL ret = 0;
    for(int i = 1; i<= n; i++) {
        LL tmp = b[num[i]];
        ret += tmp; ret %= MOD;
    }
    ret *= powmod(n, MOD-2, MOD); //n^(MOD - 2) 就是逆元
    ret %= MOD;
    return ret;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int t, time = 0;
    scanf("%d", &t);
    b[0] = 1;
    for(int i = 1; i<=40001; i++) {
        b[i] = 2*b[i-1];
        if(b[i]>= MOD) b[i] -= MOD;
    }
    while( t-- ) {
        scanf("%I64d", &n);
        for(LL i = 1; i <= n; i++) {
            scanf("%I64d", &num[i]);
        }
        LL ans = sol();
        printf("Case %d:", ++time);
        printf("%I64d\n", ans);
    }
	return 0;
}
2) 2' code: 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
#define MOD 1000003
#define N 41111
LL n, num[N], b[N];
LL egcd(LL a, LL b, LL &x, LL &y)
{
    if(!b) {
        x = 1; y = 0; return a;
    }
    LL gcd = egcd(b, a%b, x, y);
    LL t = x; x = y; y = t - (a / b) * y;
    return gcd;
}
LL Inval(LL a, LL m)
{
    LL x, y;
    egcd(a, m, x, y);
    return (x%m + m) % m;
}
LL sol()
{
    LL ret = 0;
    for(int i = 1; i<= n; i++) {
        LL tmp = b[num[i]];
        ret += tmp; ret %= MOD;
    }
    ret = ret *Inval(n, MOD);
    ret %= MOD;
    return ret;
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif // ONLINE_JUDGE
    int t, time = 0;
    scanf("%d", &t);
    b[0] = 1;
    for(int i = 1; i<=40001; i++) {
        b[i] = 2*b[i-1];
        if(b[i]>= MOD) b[i] -= MOD;
    }
    while( t-- ) {
        scanf("%I64d", &n);
        for(LL i = 1; i <= n; i++) {
            scanf("%I64d", &num[i]);
        }
        LL ans = sol();
        printf("Case %d:", ++time);
        printf("%I64d\n", ans);
    }
	return 0;
}




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值