「HDU6036-Division Game」题解
Division Game
sol
挺有意思一道题。
我们考虑在第 \(i\) 点停下等价于什么。
假如第 \(i\) 点在 \(x\) 次操作后停下,那么在它前面的点在 \(x\) 次操作后没有变为 \(1\),而在它后面的点在 \(x-1\) 次操作后没有变为 \(1\)。
令 \(f_x\) 表示在第 \(x\) 次操作后变为 \(1\) 的方案数,显然对任一位置上的数其 \(f\) 值都是相同的,因为其初值相同。
考虑操作的意义,不难发现就是消去一些 \(p\) 的一部分 \(e\)。但考虑同一种 \(p\) 的 \(e\) 等价,所以我们需要考虑如何求方案数。
我们转化一下问题,有 \(m\) 种小球,第 \(i\) 种小球有 \(p_i\) 个,现要求把这 \(m\) 个球放入 \(x\) 个箱子中,箱子里不一定要有每一种球,但箱子里一定要有球。
我们现不考虑箱子里一定要有球的限制,令放入 \(x\) 个箱子的方案数为 \(g_x\),则这是一个经典的组合数学问题:
如有不会可以自行学习球放箱子类经典组合问题。 为了防止我自己忘了,还是简单写一下。
首先隔板法可以解决要求每个箱子里有每一种球的问题,考虑“提前往每个箱子里放一个球”,那么这时候如果忽视新放的球的话,等价于箱子里可以没有球的问题。于是直接隔板法解决即可。
然后我们简单容斥一下即可得到 \(f\),这个很简单,就不讲解了:
我们发现这个东西可以转化为卷积形式:
于是就可以 NTT 加速卷积解决。
考虑第 \(x\) 次没变为 \(0\) 的方案数,不难发现就是 \(f_{x+1}\),这是因为第 \(x+1\) 次操作变为 \(1\) 当且仅当前 \(x\) 次操作没有变为 \(1\) 且在第 \(x+1\) 次一步变为了 \(1\)。
因此我们可以写出第 \(x\) 次操作使得于第 \(i\) 点结束的方案数为:
解决了。
code
namespace NTT{
    const mint P=0,G=3,Gi=328553814;
    typedef vec<mint> poly;
    vec<int> Rt;
    void ntt(int lim,poly &a,int type){
        a.resize(lim);
        repl(i,0,lim)if(i<Rt[i])swap(a[i],a[Rt[i]]);
        for(int mid=1;mid<lim;mid<<=1){
            mint w1=((~type)?G:Gi)^((P-1)/(mid<<1));
            for(int j=0;j<lim;j+=(mid<<1)){
                mint w=1;
                repl(k,0,mid){
                    mint x=a[j+k],y=w*a[j+mid+k];
                    a[j+k]=x+y;
                    a[j+mid+k]=x-y;
                    w=w*w1;
                }
            }
        }
    }
    inline void operator*=(poly &a,poly b){
        int lim=1,l=0,len=a.size()+b.size()-1;
        while(lim<len)lim<<=1,l++;
        Rt.resize(lim);
        repl(i,0,lim)Rt[i]=(Rt[i>>1]>>1)|((i&1)<<(l-1));
        ntt(lim,a,1);ntt(lim,b,1);
        repl(i,0,lim)a[i]=a[i]*b[i];
        ntt(lim,a,-1);
        a.resize(len);
        mint inv=(mint)lim^(P-2);
        repl(i,0,a.size())a[i]*=inv;
    }
    inline void operator+=(poly &a,poly b){
        if(a.size()<b.size())a.resize(b.size());
        repl(i,0,b.size())a[i]=a[i]+b[i];
    }
    inline void operator-=(poly &a,poly b){
        if(a.size()<b.size())a.resize(b.size());
        repl(i,0,b.size())a[i]=a[i]-b[i]+P;
    }
    inline poly operator*(poly a,poly b){return a*=b,a;}
    inline poly operator+(poly a,poly b){return a+=b,a;}
    inline poly operator-(poly a,poly b){return a-=b,a;}
}using namespace NTT;
const int M=15,N=1e5+5;
int m,n;
int e[M],se;
mint jc[N<<1],iv[N<<1];
mint g[N];
#define C(n,m) (jc[n]*iv[m]*iv[(n)-(m)])
inline void Main(int T){
    se=0;
    rep(i,1,m){int p;read(p,e[i]);se+=e[i];}
    jc[0]=1;rep(i,1,se<<1)jc[i]=jc[i-1]*i;
    iv[se<<1]=1/jc[se<<1];per(i,se<<1,1)iv[i-1]=iv[i]*i;
    rep(i,1,se){
        g[i]=1;
        rep(j,1,m)g[i]*=C(e[j]+i-1,i-1);
    }
    poly x(se+1),y(se+1);
    rep(i,0,se)x[i]=g[i]*iv[i],y[i]=(i&1?P-1:1)*iv[i];
    poly f=x*y;f.resize(se+1);
    rep(i,0,se)f[i]*=jc[i];
    mint ans;
    put("Case #",0),put(T,0),put(": ",0);
    rep(i,1,n){
        ans=0;
        repl(t,1,se)ans+=(f[t+1]^(i-1))*(f[t]^(n-i+1));
        if(i==1)ans+=f[se]^n;
        put(ans,' ');
    }
    put('\n',0);
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号