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

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

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

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

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

  • 对于 \(j\ne 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\ne 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\n",i,j,dp[i][j]);
} //printf("%d %d %d\n",n,j,dp[n][j]);
} printf("%d\n",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\),这样我们可以列出这样两个方程组:

\[\begin{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\) 解出来即可。

时间复杂度 \(n\log 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\n",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;
}

Codeforces 1392H - ZS Shuffles Cards(DP+打表找规律)的更多相关文章

  1. HDU 4588 Count The Carries 数位DP || 打表找规律

    2013年南京邀请赛的铜牌题...做的非常是伤心.另外有两个不太好想到的地方.. ..a 能够等于零,另外a到b的累加和比較大.大约在2^70左右. 首先说一下解题思路. 首先统计出每一位的1的个数, ...

  2. Tetrahedron(Codeforces Round #113 (Div. 2) + 打表找规律 + dp计数)

    题目链接: https://codeforces.com/contest/166/problem/E 题目: 题意: 给你一个三菱锥,初始时你在D点,然后你每次可以往相邻的顶点移动,问你第n步回到D点 ...

  3. Codeforces 193E - Fibonacci Number(打表找规律+乱搞)

    Codeforces 题目传送门 & 洛谷题目传送门 蠢蠢的我竟然第一眼想套通项公式?然鹅显然 \(5\) 在 \(\bmod 10^{13}\) 意义下并没有二次剩余--我真是活回去了... ...

  4. codeforces#1090 D. New Year and the Permutation Concatenation(打表找规律)

    题意:给出一个n,生成n的所有全排列,将他们按顺序前后拼接在一起组成一个新的序列,问有多少个长度为n的连续的子序列和为(n+1)*n/2 题解:由于只有一个输入,第一感觉就是打表找规律,虽然表打出来了 ...

  5. Codeforces Round #493 (Div. 2)D. Roman Digits 第一道打表找规律题目

    D. Roman Digits time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  6. Codeforces Beta Round #24 D. Broken robot (打表找规律)

    题目链接: 点击我打开链接 题目大意: 给你 \(n,j\),再给出 \(m[0]\) 的坐标和\(a[0]-a[n-1]\) 的坐标. 让你输出 \(m[j]\) 的坐标,其中 \(m[i]\) 和 ...

  7. 打表找规律C - Insertion Sort Gym - 101955C

    题目链接:https://cn.vjudge.net/contest/273377#problem/C 给你 n,m,k. 这个题的意思是给你n个数,在对前m项的基础上排序的情况下,问你满足递增子序列 ...

  8. Nowcoder 练习赛 17 C 操作数 ( k次前缀和、矩阵快速幂打表找规律、组合数 )

    题目链接 题意 :  给定长度为n的数组a,定义一次操作为: 1. 算出长度为n的数组s,使得si= (a[1] + a[2] + ... + a[i]) mod 1,000,000,007: 2. ...

  9. hdu 3032 Nim or not Nim? (SG函数博弈+打表找规律)

    Nim or not Nim? Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Sub ...

随机推荐

  1. Java编程开发学习路线图(附所有免费课程+在线自测)

    转自  https://yq.aliyun.com/articles/134286?spm=5176.100239.0.0.1UfveS 摘要: 长期以来,Java一直占据TIOBE编程语言排行版第一 ...

  2. 力扣 - 剑指 Offer 57. 和为s的两个数字

    题目 剑指 Offer 57. 和为s的两个数字 思路1(哈希表) 这题首先想到的是使用两个for遍历,查找是哪两个相加等于target,但是时间复杂度确实\(O(N^2)\),时间复杂度太高,因此我 ...

  3. Noip模拟13 2021.7.13:再刚题,就剁手&&生日祭

    T1 工业题 这波行列看反就非常尴尬.....口糊出所有正解想到的唯独行列看反全盘炸列(因为和T1斗智斗勇两个半小时...) 这题就是肯定是个O(n+m)的,那就往哪里想,a,b和前面的系数分开求,前 ...

  4. 2021.8.12考试总结[NOIP模拟37]

    T1 数列 考场上切掉的简单题. $a$,$b$与数列中数的正负值对答案无关.全当作正数计算即可. $exgcd$解未知数系数为$a$,$b$,加和为$gcd(a,b)$的不定方程组,再枚举每个数.如 ...

  5. stm32驱动超声波模块

    下面是关于stm32驱动超声波模块的一段代码,有需要的朋友可以复制参考,希望对大家能够有所帮助和启发. #define HCSR04_PORT GPIOB #define HCSR04_CLK RCC ...

  6. SVN查看项目修改记录及修改内容

    工具/原料 svn 一,查看修改记录 1 选择要查看的文件夹,打开之后在空白的地方右键. 2 选择svn里面的"查看日志".show_Log 3 在弹出的日志框里,可以看到,你可以 ...

  7. 转:进行vivado开发时,Generate Bitstream报错[DRC NSTD-1],详细解决步骤

    报错如下 [Drc 23-20] Rule violation (NSTD-1) Unspecified I/O Standard - 4 out of 142 logical ports use I ...

  8. 斐波那契数列 牛客网 剑指Offer

    斐波那契数列 牛客网 剑指Offer 题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0). n<=39 class Solution: ...

  9. sum-root-to-leaf-numbers leetcode C++

    Given a binary tree containing digits from0-9only, each root-to-leaf path could represent a number. ...

  10. 如何减小微信小程序代码包大小

    原作于:https://captnotes.com/how_to_reduce_package_size_of_weapp 这两天被小程序代码包大小暴涨的问题困扰了挺久.简单说说怎么回事吧,就是之前好 ...