• CF1194F Crossword Expert(数论,组合数学)


    不难的一题。不知道为什么能 $2500$……

    不过场上推错了一直不会优化……

    首先考虑 $f_i$ 表示恰好做完前 $i$ 道题的概率。

    这样很难算。修改一下,$f_i$ 表示做完至少 $i$ 道题的概率。

    答案就是 $sumlimits_{i=0}^ni(f_i-f_{i+1})=sumlimits_{i=1}^nf_i$。

    由于每道题只可能多用至多一秒,考虑 $dp[i][j]$ 为前 $i$ 道题恰好SB $j$ 次的概率。

    初始状态是 $dp[0][0]=1$。转移是 $dp[i][j]=dfrac{1}{2}(f[i-1][j]+f[i-1][j-1])$。

    盯着式子看不难看出 $dp[i][j]=(dfrac{1}{2})^idbinom{i}{j}$。用实际意义也不难理解。

    (场上就是这里推成了 $(dfrac{1}{2})^{i+j}dbinom{i+j}{i}$ 就自闭了……)

    那么有 $f_i=sumlimits_{j=0}^{r_i}dp[i][j]=(dfrac{1}{2})^isumlimits_{j=0}^{r_i}dbinom{i}{j}$。其中 $r_i=T-sumlimits_{j=1}^it_j$,表示最多允许SB几次。(其实要和 $i$ 取个 $min$,但是不影响,可以想一想为什么)

    问题就是求 $sumlimits_{j=0}^{r_i}dbinom{i}{j}$ 了。接下来是一个很妙的做法。

    首先 $i=1$ 时直接暴力。

    然后 $sumlimits_{j=0}^{r_i}dbinom{i+1}{j}=sumlimits_{j=0}^{r_i}(dbinom{i}{j}+dbinom{i}{j-1})=2sumlimits_{j=0}^{r_i}dbinom{i}{j}-dbinom{i}{r_i}$。可以直接递推。

    由于 $r_i$ 单调递减,递推完之后把 $r_{i+1}+1$ 到 $r_i$ 的组合数都删掉就行了。

    实现优秀一点可以做到 $O(n)$。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=200020,mod=1000000007,inv2=500000004;
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline ll read(){
        ll x=0,f=0;char ch=getchar();
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,t[maxn],fac[maxn],inv[maxn],invfac[maxn],f[maxn],ans,pro=1;
    ll r[maxn];
    int C(int n,ll m){
        if(n<m) return 0;
        return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
    }
    int main(){
        n=read();r[0]=read();
        FOR(i,1,n) t[i]=read();
        FOR(i,1,n) r[i]=r[i-1]-t[i];
        fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1;
        FOR(i,2,n){
            fac[i]=1ll*fac[i-1]*i%mod;
            inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
            invfac[i]=1ll*invfac[i-1]*inv[i]%mod;
        }
        FOR(i,0,min(1ll,r[1])) f[1]=(f[1]+C(1,i))%mod;
        FOR(i,2,n){
            if(r[i]<0) break;
            f[i]=(2ll*f[i-1]-C(i-1,r[i-1])+mod)%mod;
            ROF(j,min<ll>(i,r[i-1]),r[i]+1) f[i]=(f[i]-C(i,j)+mod)%mod;
        }
        FOR(i,1,n){
            if(r[i]<0) break;
            pro=1ll*pro*inv2%mod;
            ans=(ans+1ll*pro*f[i])%mod;
        }
        printf("%d
    ",ans);
    }
    View Code
  • 相关阅读:
    2016/9/18结对编程之需求分析与原型设计。
    K米评测
    软件工程的实践项目课程的自我目标
    url学习1
    调研构建之法指导下的作品
    初次尝试对接
    第二次结对编程作业——毕业导师智能匹配
    uml
    Qt中采用cairo将图片导出至PDF
    SQL删除重复的记录(只保留一条)
  • 原文地址:https://www.cnblogs.com/1000Suns/p/11203600.html
Copyright © 2020-2023  润新知