zoukankan      html  css  js  c++  java
  • Codeforces 1392H

    Codeforces 题面传送门 & 洛谷题面传送门

    真·两天前刚做过这场的 I 题,今天模拟赛就考了这场的 H 题,我怕不是预言带师

    提供一种奇怪的做法,来自于同机房神仙们,该做法不需要 Min-Max 容斥,也不用爆推组合数,只需要比较强的眼力的初中数学求解二元一次方程组知识

    期望题没往 Min-Max 容斥的方向去想,不愧是我(大雾

    首先我们先考虑一些复杂度比较高的多项式复杂度做法。注意到对于任何一个局面而言,我们并不用关心 (S) 里究竟具体有哪些数,也不用关心牌堆中具体有哪些数字牌,我们只用关心有多少数在 (S) 中,以及牌堆中还剩多少张牌。因此我们可以设 (dp_{i,j}) 表示牌堆中还剩 (i) 张牌,(S) 中已经有 (j) 个数,期望还需多少步,那么:

    • 对于 (j e n),下一次摸出一张牌,有三种可能:

      • 摸出一张数字牌,且不在 (S) 中,概率 (dfrac{n-j}{i+m}),并且会到达状态 (dp_{i-1,j+1}),因此 (dp_{i,j}leftarrow dp_{i-1,j+1}·dfrac{n-j}{i+m})
      • 摸出一张数字牌,且在 (S) 中,概率 (dfrac{i+j-n}{i+m}),并且会到达状态 (dp_{i-1,j}),因此 (dp_{i,j}leftarrow dp_{i-1,j}·dfrac{i+j-n}{i+m})
      • 摸出一张鬼牌,概率 (dfrac{m}{i+m}),并且会到达状态 (dp_{n,j}),因此 (dp_{i,j}leftarrow dp_{n,j}·dfrac{m}{i+m})

      别忘了加上最后的 (1),因此对于 (j e 0) 的情况我们有转移方程 (dp_{i,j}=dp_{i-1,j+1}·dfrac{n-j}{i+m}+dp_{i-1,j}·dfrac{i+j-n}{i+m}+dp_{n,j}·dfrac{m}{i+m}+1)

    • 对于 (j=n)​ 的情况就比较 trivial 了,如果摸出一张数字牌那么会到达 (dp_{i-1,j})​,否则直接结束,因此 (dp_{i,n}=dp_{i-1,n}·dfrac{i}{i+m}+1)

    最终答案即为 (dp_{n,0})

    由于 DP 转移存在后效性,因此直接转移不可取,考虑高斯消元,直接高斯消元是六方的,不过注意到对于一个 (dp_{i,j}) 而言,如果我们倒着枚举 (j),那么我们就只用对 (j) 相同的这一行的值进行高斯消元,复杂度 (n^4)。还可以进一步优化,就是注意到在我们倒序枚举的过程中 (dp_{i-1,j+1}·dfrac{n-j}{i+m}) 是常数不用管它,如果我们不考虑这个 (dp_{n,j}) 那转移关系不成环就不存在后效性,而加上这个 (dp_{n,j}),由于导致这个后效性的只有 (dp_{n,j}),我们就可以考虑将所有 (dp_{i,j}) 都表示成 (sdp_{n,j}+t) 的形式,这样顺着一遍推过去,最后可以得到 (dp_{n,j}=sdp_{n,j}+t),解出 (dp_{n,j}) 后再推回去即可,这个套路可以在这道题中找到,时间复杂度 (n^2),反正还是过不去(

    附:(n^2) 的代码:

    const int MAXN=1000;
    const int MOD=998244353;
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
    	return ret;
    }
    int n,m,dp[MAXN+5][MAXN+5];
    int main(){
    //	freopen("toad.in","r",stdin);
    //	freopen("toad.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	dp[0][n]=1;
    	for(int i=1;i<=n;i++) dp[i][n]=(1ll*i*qpow(i+m,MOD-2)%MOD*dp[i-1][n]+1)%MOD;
    	for(int j=n-1;~j;j--){
    		int cs=0,ct=0;
    		for(int i=n-j;i<=n;i++){
    			int coef1=1ll*m*qpow(i+m,MOD-2)%MOD;
    			int coef2=1ll*(i+j-n+MOD)*qpow(i+m,MOD-2)%MOD;
    			int coef3=1ll*(n-j)*qpow(i+m,MOD-2)%MOD;
    			cs=1ll*cs*coef2%MOD;ct=1ll*(ct+1)*coef2%MOD;
    			ct=(ct+1ll*(dp[i-1][j+1]+1)*coef3)%MOD;
    			cs=(cs+coef1)%MOD;ct=(ct+coef1)%MOD;
    		} dp[n][j]=1ll*ct*qpow((1-cs+MOD)%MOD,MOD-2)%MOD;//dp[n][j]=cs*dp[n][j]+ct
    		for(int i=n-j;i<n;i++){
    			int coef1=1ll*m*qpow(i+m,MOD-2)%MOD;
    			int coef2=1ll*(i+j-n+MOD)*qpow(i+m,MOD-2)%MOD;
    			int coef3=1ll*(n-j)*qpow(i+m,MOD-2)%MOD;
    			dp[i][j]=(1ll*(dp[i-1][j]+1)*coef2+1ll*(dp[i-1][j+1]+1)*coef3%MOD+1ll*(dp[n][j]+1)*coef1)%MOD;
    //			printf("%d %d %d
    ",i,j,dp[i][j]);
    		} //printf("%d %d %d
    ",n,j,dp[n][j]);
    	} printf("%d
    ",dp[n][0]);
    	return 0;
    }
    

    接下来考虑进一步优化。这里就要一些观察了,打个表可以发现,对于 (j) 相同的 (dp_{i,j}) 而言随着 (i) 的增大 (dp_{i,j})​ 成等差数列,换句话说所有 (dp_{i,j}) 都可以写成 (k_ji+b_j) 的形式。证明不会,大概可以归纳(?)(大概就发现 (dp_{i,n}) 是等差数列,而 (dp_{i,j}) 只从 (dp_{i,j+1}) 推来,这就天然地形成了归纳的模型,但具体怎么归纳我也没想出来)。这样对于每一个 (j),我们只用确定 (dp_{n,j})(dp_{n-1,j}),所有 (dp_{i,j}) 都确定了,方便起见这里假设 (dp_{i,j}=y_j(n-i)+x_j),这样我们可以列出这样两个方程组:

    [egin{cases} x_j=dp_{n-1,j+1}·dfrac{n-j}{n+m}+(x_j+y_j)·dfrac{j}{n+m}+x_j·dfrac{m}{n+m}+1\ x_j+y_j=dp_{n-2,j+1}·dfrac{n-j}{n+m-1}+(x_j+2y_j)·dfrac{j-1}{n+m-1}+x_j·dfrac{m}{n+m-1}+1 end{cases} ]

    (x_j,y_j) 解出来即可。

    时间复杂度 (nlog n)好像做不到 (mathcal O(n))

    const int MAXN=2e6;
    const int MOD=998244353;
    int qpow(int x,int e){
    	int ret=1;
    	for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
    	return ret;
    }
    int n,m,x[MAXN+5],y[MAXN+5],f[MAXN+5];
    int calc(int i,int j){return (x[j]+1ll*(n-i)*y[j]%MOD)%MOD;}
    int main(){
    	scanf("%d%d",&n,&m);int ivn=qpow(n+m,MOD-2),ivn1=qpow(n-1+m,MOD-2);
    	f[0]=1;for(int i=1;i<=n;i++) f[i]=(1ll*i*qpow(i+m,MOD-2)%MOD*f[i-1]+1)%MOD;
    	x[n]=f[n];y[n]=(f[n-1]-f[n]+MOD)%MOD;
    	for(int j=n-1;j;j--){
    		int a1=1ll*(n-j)*ivn%MOD;
    		int b1=(MOD-1ll*j*ivn%MOD)%MOD;
    		int c1=1ll*(n-j)*ivn%MOD*calc(n-1,j+1)%MOD;
    		int a2=1ll*(n-j)*ivn1%MOD;
    		int b2=(1-1ll*(j-1+MOD)*2*ivn1%MOD+MOD)%MOD;
    		int c2=1ll*(n-j)*ivn1%MOD*calc(n-2,j+1)%MOD;
    		(c1+=1)%=MOD;(c2+=1)%=MOD;
    		y[j]=1ll*(1ll*c1*a2%MOD-1ll*c2*a1%MOD+MOD)*qpow((1ll*b1*a2%MOD-1ll*b2*a1%MOD+MOD)%MOD,MOD-2)%MOD;
    		x[j]=1ll*(c1-1ll*y[j]*b1%MOD+MOD)*qpow(a1,MOD-2)%MOD;
    	} printf("%d
    ",1ll*(1ll*calc(n-1,1)*n%MOD*ivn%MOD+1ll*m*ivn%MOD)*qpow(n,MOD-2)%MOD*(n+m)%MOD+1);
    	return 0;
    }
    
  • 相关阅读:
    Binary Tree Inorder Traversal
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Majority Element
    Excel Sheet Column Number
    Reverse Bits
    Happy Number
    House Robber
    Remove Linked List Elements
    Contains Duplicate
  • 原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-1392H.html
Copyright © 2011-2022 走看看