zoukankan      html  css  js  c++  java
  • Codeforces

    https://codeforc.es/contest/1194/problem/F

    下面是错的。

    看起来有点概率dp的感觉?
    给你T秒钟时间,你要按顺序处理总共n个事件,每个事件处理花费的时间是ti秒钟,有一半的概率失手导致多花1秒钟。求T时间内处理完事情的总数的期望。

    处理完第1个事件,有0.5概率花t1,有0.5概率花t1+1。
    处理完第2个事件,有0.25概率花t1+t2,有0.5概率花t1+t2+1,有0.25概率花t1+t2+2。
    处理完第3个事件,有0.125概率花t1+t2+t3,有0.375概率花t1+t2+t3+1,有0.375概率花t1+t2+t3+2,有0.125概率花t1+t2+t3+3。


    一开始的思路不对,一开始是按“最后一个结束的元素”来分类,这样子导致非常难计算,应该独立算每个元素的贡献概率。


    答案(E=sumlimits_{i=1}^{n}P(i))(P(i))表示第(i)个元素贡献的概率。在前面的很多个里面这样的概率是1。

    先累计就算每一次都失手还是能做完的前面的若干个。记第一个不一定能够做完的为x。


    第一步,统计结束于(x)的,失手次数的上限(d_x)不能超过(x),也要满足(pre_x+d_x<=T),即不能超过(T-pre_x)。还要保证最后下一个元素(x+1)不能被选到吗?其实并不需要这么做,只要在选下一个的时候加上就可以了。

    (d_x=min(x,T-pre_x))

    失手([0,d_x])次会导致贡献第(x),这样的贡献是

    (P(x)=(frac{1}{2})^{x}sumlimits_{i=0}^{d_x}C_x^i)


    第二步,统计结束于(x+1)的,失手次数(d_{x+1})不能超过(x+1),也要满足(pre_{x+1}+d_{x+1}<=T),即不能超过(T-pre_{x+1})

    (d_{x+1}=min(x+1,T-pre_{x+1}))

    失手([0,d_{x+1}])次会导致贡献(x+1),这样的贡献是

    (P(x+1)=(frac{1}{2})^{x+1}sumlimits_{i=0}^{d_{x+1}}C_{x+1}^i)


    最后是一个奇怪的效率问题了。应该也是这道题第二值得学习的地方(第一值得学习的是统计的思路)。

    第一个是要注意到组合数的一个变形:

    (C_n^k+C_{n}^{k+1}=C_{n+1}^{k+1}),也就是按第(n+1)个元素有没有被选进这(k+1)个元素分类。

    所以

    (sumlimits_{i=0}^{k+1}C_{n+1}^i=C_{n}^{k+1}+C_{n}^{k}+C_{n}^{k}+C_{n}^{k-1}+...+C_{n}^{1}+C_{n}^{0}+C_{n}^{0})

    也就是:

    (sumlimits_{i=0}^{k+1}C_{n+1}^i=C_{n}^{k+1}+2*sumlimits_{i=0}^{k}C_{n}^i)

    第二个是要注意到(d_{x+1}<=d_{x}+1),这不是很显然吗,多一件事情肯定要花多至少1秒去处理,就必须少失误一次?这个东西可以保证,上标是单调下降的。

    所以可以O(n)转移出所有需要的组合数。因为上下标都是单调的。


    还是有了两处溢出错误以及一个T==0也是合法情况的问题。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int mod = 1e9 + 7;
    const int inv2 = (mod + 1) >> 1;
    
    int n, curn, curk;
    ll T, sigmaC, E, inv2x;
    
    const int MAXN = 2e5;
    
    ll inv[MAXN + 5], fac[MAXN + 5], invfac[MAXN + 5];
    
    void init_inv(int n = MAXN) {
        inv[1] = 1;
        for(int i = 2; i <= n; i++) {
            inv[i] = inv[mod % i] * (mod - mod / i) % mod;
        }
    }
    
    void init_fac_invfac(int n = MAXN) {
        init_inv(n);
        fac[0] = 1, invfac[0] = 1;
        for(int i = 1; i <= n; i++) {
            fac[i] = fac[i - 1] * i % mod;
            invfac[i] = invfac[i - 1] * inv[i] % mod;
        }
    }
    
    inline ll C(ll n, ll m) {
        if(n < m)
            return 0;
        return fac[n] * invfac[n - m] % mod * invfac[m] % mod;
    }
    
    int t[200005];
    ll pret[200005];
    
    void calc_sigmaC(int x) {
        ll tmp = 0;
        int dx = min((ll)x, T - pret[x]);
        for(int i = 0; i <= dx; i++) {
            tmp += C(x, i);
            if(tmp>=mod)
                tmp-=mod;
        }
        sigmaC = tmp;
        curn = x;
        curk = dx;
    }
    
    void next_sigmaC(int nextx) {
        int dx1 = min((ll)nextx, T - pret[nextx]);
        ll tmp = sigmaC;
        tmp = 2ll * tmp % mod;
        tmp = (tmp + C(curn, curk + 1)) % mod;
        curn++;
        curk++;
        while(curk > dx1) {
            tmp = (tmp + mod - C(curn, curk)) % mod;
            curk--;
        }
        sigmaC = tmp;
    }
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
        //freopen("Yinku.out", "w", stdout);
    #endif // Yinku
        init_fac_invfac();
        while(~scanf("%d%lld", &n, &T)) {
            pret[0] = 0;
            for(int i = 1; i <= n; i++) {
                scanf("%d", &t[i]);
                pret[i] = pret[i - 1] + t[i];
            }
            E = 0;
            inv2x = inv2; //与x配对的sigmaC的系数
            int x = 1;
            while(x <= n && pret[x] + x <= T) {
                x++;
                E++;
                inv2x = (inv2x * inv2) % mod;
            }
            //x现在是第一个不一定可以的
            if(x <= n) {
                calc_sigmaC(x);
                for(; x <= n; x++) {
                    E = (E + inv2x * sigmaC % mod) % mod;
                    if(T - pret[x + 1] < 0)
                        break;
                    next_sigmaC(x + 1);
    
                    inv2x = (inv2x * inv2) % mod;
                }
            }
            printf("%lld
    ", E);
        }
    }
    

    需要学习的是分类统计的思路,以及组合数直接转移的思想。

  • 相关阅读:
    实验四 代码评审
    UML 建模工具的安装与使用
    结对编程(阶段二)
    结对编程第一阶段
    实验一 GIT 代码版本管理
    【Alpha冲刺阶段】Scrum Meeting Daily5
    【Alpha冲刺阶段】Scrum Meeting Daily2
    个人项目作业
    个人介绍+软工5问
    SpringMVC拦截html页面访问
  • 原文地址:https://www.cnblogs.com/Yinku/p/11189287.html
Copyright © 2011-2022 走看看