zoukankan      html  css  js  c++  java
  • 「CTS2019 | CTSC2019」随机立方体 解题报告

    「CTS2019 | CTSC2019」随机立方体

    据说这是签到题,但是我计数学的实在有点差,这里认真说一说。


    我们先考虑一些事实

    • 如果我们在位置((x_0,y_0,z_0))钦定了一个极大数(p),那么我们需要把(x=x_0)(y=y_0)(z=z_0)的三个平面的交中填上比(p)小的数字,这样,剩下的正方体就成了一个长宽高分别为((n-1)(m-1)(l-1))的子问题了。
    • 考虑到我们使用的是数字的相对大小关系,而不是数字的值,也就是说,任意的(k)个数字,发挥的作用是完全一样的,他们互不相同。

    以上两个事实都可以启发我们往容斥的方向想,考虑容斥。

    (dp_i)为至少有(i)个极大数字的方案数,然后我们进行钦定。

    (dp_i)等于(钦定(i)个极大数的位置方案数)( imes)(极大数删去平面的交的填充方案数)( imes)(剩下随意发挥的剩余正方体填充方案数)

    首先我们需要把所有的数字分成两部分,删去的平面交与剩余的正方体。

    这里设(N=n imes m imes l),(g_i)为钦定(i)个极大值后删去平面交的方块数,那么这个数字的划分方案数就是(inom{N}{g_i})

    (g_i)的表达式也很简单,(g_i=nml-(n-i)(m-i)(l-i))

    然后挨个考虑(dp)的组成成分,设(f_i)等于钦定(i)个极大的位置的方案数,因为每次都是子问题,所以实际上很简单

    [f_i=prod_{j=0}^{i-1}(n-j)(m-j)(l-j) ]

    (h_i)表示删去的平面交的填充方案数,可以发现,这个东西一下子求不来,可以考虑一下递推的方法。

    我们再考虑一个事实

    • 如果先钦定了(x),然后删去了相关平面,再钦定(y),如果我们想让删去的平面中的填充不影响(y),我们需要满足条件(x<y)

    这启发我们先进行数值大的外面的填充

    我们先把(g_i)个数中最大的数值拿出来,填到对应位置,然后剩下的(g_i-g_{i-1}+1)个位置(就是第(i)次删去的平面),每个位置可以随便选择一个数,方案数为(frac{(g_i-1)!}{g_{i-1}!}),然后我们可以发现,剩下的数的填充就是子问题(h_{i-1})

    于是我们可以得到

    [egin{aligned} h_i&=frac{(g_i-1)!}{g_{i-1}!} imes h_{i-1}\ &=prod_{j=0}^{i-1}frac{(g_{j+1}-1)!}{g_j!} end{aligned} ]

    最后面随意填充就简单了就是((N-g_i)!)

    然后我们带回去化简一下

    [egin{aligned} dp_i&=inom{N}{g_i}f_ih_i(N-g_i)!\ &=N!f_iprod_{j=1}^{i}(g_j-1)!prod_{j=0}^{i}frac{1}{g_j!}\ &=N!f_iprod_{j=1}^{i}frac{1}{g_j} end{aligned} ]

    我们现在求的是方案数,但题意是概率。所以这个(N!)其实直接扔了就好。

    (dp_i)代表至少(i)个极大数的概率,那么

    [dp_i=f_iprod_{j=1}^ifrac{1}{g_j} ]

    我们知道,如果(a imes b=c),那么可以有

    [a^{-1}equiv c^{-1}bpmod p ]

    有了这个,我们就可以线性求(g)的逆元了,这也是我们熟知的阶乘逆元的方法。

    还有最后一步,设(ans_i)为恰好(i)个极大数的概率

    (mi=min(n,m,l))

    我们有

    [dp_i=sum_{j=i}^{mi}ans_jinom{j}{i} ]

    由二项式反演可得

    [ans_i=sum_{j=i}^{mi}dp_jinom{j}{i}(-1)^{j-i} ]


    什么?你不会二项式反演?(其实我自己忘了就尝试推了一会儿,但我并不会比较感性理解的,只会一个推式子的

    [A_i=sum_{j=i}^ninom{j}{i}B_j ]

    (delta_{i,j})为满足

    [B_i=sum_{j=i}^ndelta_{i,j}A_j ]

    的东西,考虑求出Ta

    [egin{aligned} B_i&=sum_{j=i}^ndelta_{i,j}A_j\ &=sum_{j=i}^ndelta_{i,j}sum_{k=j}^ninom{k}{j}B_k\ &=sum_{k=i}^nB_ksum_{j=i}^kdelta_{i,j}inom{k}{j} end{aligned} ]

    可以得到,我们需要满足

    [sum_{j=i}^kdelta_{i,j}inom{k}{j}=[k=i] ]

    在优秀的莫比乌斯反演的某步证明中,我们用到了这个式子

    [sum_{i=0}^n(-1)^iinom{n}{i}=[n=0] ]

    证明方法很多,不说了

    随便把这个式子变一变

    [sum_{j=i}^k(-1)^{j-i}inom{k-i}{k-j}=[k=i] ]

    那么我们可以得到

    [delta_{i,j}inom{k}{j}=(-1)^{j-i}inom{k-i}{k-j} ]

    然后你解出来就可以了,但是我们平常写起来其实应该不是这样的

    注意到一个组合式

    [inom{k}{j}inom{j}{i}=inom{k}{i}inom{k-i}{k-j} ]

    这个证明方法也多,你拆开或者组合意义理解都行

    [inom{k}{i}sum_{j=i}^k(-1)^{j-i}inom{k-i}{k-j}=[k=i] ]

    就是把这个加到前面并不会影响什么

    于是我们有

    [delta_{i,j}=(-1)^{j-i}inom{j}{i} ]

    综上

    [B_i=sum_{j=i}^n(-1)^{j-i}inom{j}{i}A_j ]


    Code:

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    using std::min;
    const int SIZE=1<<21;
    char ibuf[SIZE],*iS,*iT;
    //#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
    #define gc() getchar()
    template <class T>
    void read(T &x)
    {
    	x=0;char c=gc();
    	while(!isdigit(c)) c=gc();
    	while(isdigit(c)) x=x*10+c-'0',c=gc();
    }
    const int mod=998244353;
    int inline add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    #define mul(x,y) (1ll*(x)*(y)%mod)
    int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;}
    const int N=5e6+1;
    int fac[N],inv[N],dp[N],f[N],g[N],invg[N],yuy[N];
    int C(int m,int n){return mul(mul(fac[m],inv[n]),inv[m-n]);}
    int main()
    {
    	fac[0]=1;for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
    	inv[N-1]=qp(fac[N-1],mod-2);
    	for(int i=N-2;~i;i--) inv[i]=mul(inv[i+1],i+1);
    	int T,n,m,l,k;read(T);
    	while(T--)
    	{
    		read(n),read(m),read(l),read(k);
    		int mi=min(n,min(m,l));
    		if(k>mi)
    		{
    			puts("0");
    			continue;
    		}
    		f[0]=1;
    		for(int i=0;i<mi;i++) f[i+1]=mul(f[i],yuy[i]=mul(n-i,mul(m-i,l-i)));
    		int fg=1,tot=yuy[0];
    		yuy[mi]=0;
    		for(int i=1;i<=mi;i++)
    		{
    			g[i]=add(tot,mod-yuy[i]);
    			fg=mul(fg,g[i]);
    		}
    		invg[mi]=qp(fg,mod-2);
    		for(int i=mi-1;~i;i--) invg[i]=mul(invg[i+1],g[i+1]);
    		for(int i=1;i<=mi;i++) dp[i]=mul(f[i],invg[i]);
    		int ans=0;
    		for(int i=k;i<=mi;i++)
    		{
    			if(i-k&1) ans=add(ans,mod-mul(C(i,k),dp[i]));
    			else ans=add(ans,mul(C(i,k),dp[i]));
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    2019.5.20

  • 相关阅读:
    字符串及课堂例子整理
    大道至简:软件工程实践者的思想——第四章感想
    大道至简:软件工程实践者的思想——第一章感想(重写)
    大道至简:软件工程实践者的思想——第三章感想
    大道至简:软件工程实践者的思想——第二章感想和课后思考
    简单程序代码及心得体会
    《大 道 至 简   ——软件工程实践者的思想 》是懒人造就了方法读后感
    实验报告
    懒人的思考造就了方法
    编程其实很简单
  • 原文地址:https://www.cnblogs.com/butterflydew/p/10895448.html
Copyright © 2011-2022 走看看