【模拟】【高精度乘法】【NOI2006】神奇口袋


Input
第一行有三个正整数t,n,d;第二行有t个正整数a1,a2,…,at,表示游戏开始时口袋里t种颜色的球,每种球的个数。以下n行,每行有两个正整数xi,yi,表示第xi次抽出颜色为的yi球。
Output
要求用分数形式输出(显然此概率为有理数)。输出文件包含一行,格式为:分子/分母。同时要求输出最简形式(分子分母互质)。特别的,概率为0应输出0/1,概率为1应输出1/1。
Sample Input 1
2 3 1
1 1
1 1
2 2
3 1
Sample Output 1
1/12
Sample Input 2
3 1 2
1 1 1
5 1
Sample Output 2
1/3
 

这道题目看起来似乎无从下手,但仔细观察则可以发现其中可以作等效处理。先看一个证明:

有了以上结论,就可以将每次的概率直接累乘(至于约分,可以筛出20000以内的素数表,然后保存分子分母的各个因数)。当然,用高精度是显然的。代码:

/****************************\
 * @prob: NOI2006 bag       *
 * @auth: Wang Junji        *
 * @stat: Accepted.         *
 * @date: June. 2nd, 2012   *
 * @memo: 模拟、高精度乘法    *
\****************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>

const int maxN = 20010, BIT = 10000;
bool tag[maxN];
int p[maxN], cnt_num[maxN], cnt_den[maxN], ball[maxN], top, T, n, D;

class Huge
{
private: int len, ele[maxN];
public:
    Huge(int x) {for (len = 0; x; x /= BIT) ele[len++] = x % BIT;}
    Huge(): len(1) {memset(ele, 0, sizeof ele);}
    Huge &operator*=(const int &x)
    {
        for (int i = 0; i < len; ++i) ele[i] *= x;
        for (int i = 0; i < len; ++i) ele[i + 1] += ele[i] / BIT, ele[i] %= BIT;
        while (ele[len]) ++len; return *this;
    }
    void print()
    {
        printf("%d", ele[len - 1]);
        for (int i = len - 2; i > -1; --i) printf("%04d", ele[i]);
        return;
    }
} num(1), den(1);

inline void update(int *cnt, int x)
{
    for (int i = 0; x && i < top; ++i)
        for (; x % p[i] == 0; x /= p[i]) ++cnt[i];
    return;
}

inline void mkprime()
{
    for (int i = 2; i < maxN; ++i)
    if (!tag[i]) p[top++] = i;
    else for (int j = 0; j < top && p[j] * i < maxN; ++j)
    {tag[i * p[j]] = 1; if (i % p[j] == 0) break;}
    return;
}

int main()
{
    freopen("bag.in", "r", stdin);
    freopen("bag.out", "w", stdout);
    scanf("%d%d%d", &T, &n, &D); int sum = 0; mkprime();
    for (int i = 0; i < T; ++i) scanf("%d", ball + i), sum += ball[i];
    for (int i = 0; i < n; ++i)
    {
        int x, y; scanf("%d%d", &x, &y); --y;
        if (!ball[y]) {printf("0/1\n"); return 0;}
        update(cnt_num, ball[y]), ball[y] += D;
        update(cnt_den, sum), sum += D;
    }
    for (int i = 0, tmp; i < top; ++i)
    {
        tmp = std::min(cnt_num[i], cnt_den[i]);
        cnt_num[i] -= tmp, cnt_den[i] -= tmp;
        while (cnt_num[i]--) num *= p[i];
        while (cnt_den[i]--) den *= p[i];
    }
    num.print(); printf("/"); den.print(); return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值