【CF1194F】Crossword Expert

题目链接

大意

给你\(N\)个事件,解决每个事件所需的时间有\(1/2\)的概率为\(t[i]\)\(1/2\)的概率为\((t[i]+1)\),给你总时间\(T\),在\(T\)时间内按顺序解决事件,求能解决的事件的期望个数。
(答案对1000000007取模)

(N<=2e5 1<=t[i]<=1e9 1<=T<=2e14)

思路

考虑如何求期望:
我们设\(P[i]\)表示第\(i\)件物品能被做完的概率。
则有\[Ans=\sum_{i=1}^{N}P[i]\]

则问题就转化为如何求\(P[i]\)

我们设\(Sum[i]\)表示前\(i\)件事的最小时间和,即\(Sum[i]=\sum_{i=1}^{N}t[i]\)

①:对于\(Sum[i]+i<=T\)的情况:
则第\(i\)件事一定会被做完,故\(P[i]=1\)

②:对于\(Sum[i]<=T<Sum[i]+i\)的情况:
我们设\(Dp[i][j]\)表示前\(i\)件事有\(j\)件事时间多做了\(1\)个单位的概率,即多做了\(j\)个时间单位,
则对于每个\(Dp[i][j]\),若有\(Sum[i]+j<=T\),则可以对\(P[i]\)产生\(Dp[i][j]\)的贡献。

考虑如何求\(Dp[i][j]\)
将这些事件按是否多做\(1\)个时间单位分类,
若完成时间为\(t[i]\),则类型为\(0\)
若完成时间为\(t[i]+1\),则类型为\(1\)
则可以将这些事件的状态表示为一个\(01\)串。
则总状态数就为\(2^i\),从\(i\)个数中选\(j\)个让其状态为\(1\)的个数就为\(C(i,j)\)
\(Dp[i][j]=\frac{C(i,j)}{2^i}\)

对于每个\(P[i]\)
我们倘若每次都去枚举有哪些\(j\)是可以满足\(Sum[i]+j<=T\)的话,很明显会超时。
则考虑如何从上一次\((P[i-1])\)所需的状态数转到\(P[i]\)的状态数。
(注:第一次进入情况②的时候可以暴力找到状态)

考虑如何快速地从\(P[i-1]\)转移到\(P[i]\)的状态:
我们设上一次需要的\(C\)是从\(C(i-1,0)\)\(C(i-1,Sum_K)\)
设上一次的\(P[i-1]=\frac{Sum_N}{2^{i-1}}\),则有\(Sum_N=\sum_{j=0}^{Sum_K}C(i-1,j)\)

根据\[C(i,j)=C(i-1,j)+C(i-1,j-1)\]
则有\[\sum_{j=1}^{Sum_K}C(i,j)=\sum_{j=1}^{Sum_K}(C(i-1,j)+C(i-1,j-1))\]
\[\sum_{j=1}^{Sum_K}C(i,j)=(2*\sum_{j=0}^{Sum_K}C(i-1,j))-C(i-1,0)-C(i-1,Sum_K)\]

然后,对于这次的\(Sum_N\)来说,
\(Sum_N=\sum_{j=1}^{Sum_K}C(i,j)+C(i,0)-\sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j)\)
\(Sum_N=(2*\sum_{j=0}^{Sum_K}C(i-1,j))-C(i-1,Sum_K)-\sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j)\)
\(Sum_N=Sum_N*2-C(i-1,Sum_K)-\sum_{j=T-Sum[i]+1}^{Sum_K}C(i,j)\)
则这一次的\(Sum_N\)就可以从上一次的\(Sum_N\)转移过来。
显然这一次的\(Sum_K=T-Sum[i]\)
\(Sum_K\)会随着\(i\)的增大而减小,
而进入情况②的条件是:\(Sum[i]<=T<Sum[i]+i\)
即求解所有的\(Sum_N\)的时间复杂度总计\(O(N)\)

③:\(Sum[i]>T\)
则事件\(i\)一定不会被做完,即\(P[i]=0\)

综上,\(Ans\)得解。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int ON=200000;
const int MAXN=200005;
const long long ONE=1;
const int MOD=1000000007;
int N,t[MAXN];
long long F[MAXN];
long long T,Sum[MAXN],Ans;
long long f[MAXN]={1},fe[MAXN]={1};
long long O[MAXN]={1},Oe[MAXN]={1};
long long Sum_N,Sum_K;
LL quick_Pow(LL x,LL y){
    if(y==0)return 1;
    if(y==1)return x;
    if(y%2)return (x*quick_Pow((x*x)%MOD,y/2))%MOD;
    return quick_Pow((x*x)%MOD,y/2);
}
void Prepare(){
    for(int i=1;i<=ON;i++){
        f[i]=(f[i-1]*i)%MOD;
        fe[i]=quick_Pow(f[i],MOD-2);
        O[i]=(O[i-1]*2)%MOD;
        Oe[i]=quick_Pow(O[i],MOD-2);
    }
}
long long C(long long x,long long y){
    if(y>x)return 0;
    return (f[x]*((fe[y]*fe[x-y])%MOD))%MOD;
}
long long work(long long n,long long k){
    if(Sum_K==0){
        for(int i=0;i<=k;i++)
            Sum_N=(Sum_N+C(n,i))%MOD;
    }else{
        Sum_N=(Sum_N*2-C(n-1,Sum_K)+MOD)%MOD;
        for(int i=Sum_K;i>k;i--)
            Sum_N=(Sum_N-C(n,i)+MOD)%MOD;
    }
    Sum_K=k;
    return Sum_N;
}
int main(){
    Prepare();
    scanf("%d%lld",&N,&T);
    for(int i=1;i<=N;i++){
        scanf("%d",&t[i]);
        Sum[i]=Sum[i-1]+t[i];
    }
    for(int i=1;i<=N;i++){
        if(Sum[i]>T)break;
        if(Sum[i]+i<=T){
            F[i]=1;
            continue;
        }
        F[i]=(work(i,T-Sum[i])*Oe[i])%MOD;
    }
    for(int i=1;i<=N;i++)
        Ans=(Ans+F[i])%MOD;
    printf("%lld\n",Ans);
}

转载于:https://www.cnblogs.com/ftotl/p/11597633.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值