zoukankan      html  css  js  c++  java
  • UOJ#449. 【集训队作业2018】喂鸽子

    #449. 【集训队作业2018】喂鸽子

    DP好题

     

    法一:min-max容斥

    处理前m个,最快吃饱的鸽子期望的时间

    根据期望的定义

    考虑每个方案数的概率*期望次数

    枚举前m个用了x个,概率都是(1/m)^x*Em(x)

    而Em(x)表示往前m个扔了x个期望的总共次数,就是x*n/m

    考虑用了x个的方案数

    生成函数EGF思想。

    而出现一个有k次就会停止。最后一个位置一定会使得一个鸽子饱了。

    f[i][j]前i个,总共用了j个,没有一个有k次的方案数

    g[i][j],。。。。。。。。有一个有k次的方案数

    NTT优化转移。

    f和1/k!的项乘出来的贡献加到g里去即可。

    O(n^2klog(nk))

     

     

    法二:“有效玉米序列”

    神仙思路

    只考虑“有实质变化”的玉米,即喂给了一个没有饱的鸽子的玉米

    还是考虑每个“有效玉米序列”的贡献,就是出现概率*期望

    一个固定的“有效玉米序列”,出现概率和期望都和每次扔玉米时已经饱的鸽子有关系

    所以状态多记录上饱的鸽子数量

    至于怎样判断一个鸽子饱了

    先填“白色”有效玉米,

    想让一个鸽子饱了,就钦定之前k-1个白玉米染上色!

    所以这个白玉米还是“对未来承诺”,或者对未来预留的trick

    状态保留贡献和和概率和即可。是可以转移的。

    复杂度:O(n^2k)

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define fi first
    #define se second
    #define mk(a,b) make_pair(a,b)
    #define numb (ch^'0')
    #define pb push_back
    #define solid const auto &
    #define enter cout<<endl
    #define pii pair<int,int>
    using namespace std;
    typedef long long ll;
    template<class T>il void rd(T &x){
        char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
    template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
    template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar('
    ');}
    template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
    ');}
    namespace Modulo{
    const int mod=998244353;
    int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
    void inc(int &x,int y){x=ad(x,y);}
    int mul(int x,int y){return (ll)x*y%mod;}
    void inc2(int &x,int y){x=mul(x,y);}
    int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
    template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);}
    template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);}
    }
    using namespace Modulo;
    namespace Miracle{
    const int N=50000+5;
    int f[50*1000+5][55];
    int g[N][55];
    int jie[N],inv[N];
    int iv[N];
    int C(int n,int m){
        if(n<m||n<0||m<0) return 0;
        return mul(jie[n],inv[m],inv[n-m]);
    }
    int n,k;
    int main(){
        rd(n);rd(k);
        int lim=n*k;
        jie[0]=1;
        iv[1]=1;
        for(reg i=1;i<=lim;++i) jie[i]=mul(jie[i-1],i);
        for(reg i=2;i<=n;++i){
            iv[i]=mul(mod-mod/i,iv[mod%i]);
        }
        inv[lim]=qm(jie[lim]);
        for(reg i=lim-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
        g[0][0]=1;
        for(reg m=0;m<lim;++m){
            for(reg c=0;k*c<=m;++c){
                int ct=ad(mul(iv[n-c],f[m][c]),mul(iv[n-c],iv[n-c],n,g[m][c]));
                inc(f[m+1][c],ct);
                inc(f[m+1][c+1],mul(ct,C(m-k*c,k-1)));
                ct=mul(g[m][c],iv[n-c]);
                inc(g[m+1][c],ct);
                inc(g[m+1][c+1],mul(ct,C(m-k*c,k-1)));
            }
        }
        ll ans=mul(f[lim][n],jie[n]);
        ot(ans);
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
    */

    两种方法的共同之处是:

    都从统计每个合法方案的出现概率和期望次数统计

    考虑“有变化”的玉米

    根据需要进行DP设计

     

    第一种方法:是min-max容斥的套路。难点转化为合法的方案数。EGF思想,DP+NTT优化

    第二种方法:直接考虑“有效玉米序列”,发现概率只和之前饱的鸽子有关而进行状态设计。

    然鹅并不知道一个鸽子饱不饱,所以不能立刻决定当前玉米喂给谁。所以利用“白玉米”,最后统一染色。

  • 相关阅读:
    Codeforces Round #251 (Div. 2) A
    topcoder SRM 623 DIV2 CatAndRat
    topcoder SRM 623 DIV2 CatchTheBeatEasy
    topcoder SRM 622 DIV2 FibonacciDiv2
    topcoder SRM 622 DIV2 BoxesDiv2
    Leetcode Linked List Cycle II
    leetcode Linked List Cycle
    Leetcode Search Insert Position
    关于vim插件
    Codeforces Round #248 (Div. 2) B. Kuriyama Mirai's Stones
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10992823.html
Copyright © 2011-2022 走看看