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. 初学python-day3 列表

  2. C/C++ 数据类型 表示最大 最小数值 探讨

    C/C++中存储数字格式有整型和浮点型 字符型数据本质上也是以整型存储 整型 对于整型数据,最大值最小值很好计算 先确定对应数据型在本地所占用的字节数,同一数据型由于系统或者编译器的不同,所占字节不同 ...

  3. 使用nexus搭建一个docker私服

    使用nexus搭建docker私服 一.需求: 二.实现步骤 1.编写`docker-compose`文件,实现`nexus`的部署 2.修改/usr/lib/systemd/system/docke ...

  4. git merge远程合并

    当某个分支上的开发工作完成后需要将其合入主分支master 但是在提交合并前我们自己最好做一次衍合,目的是检测是否有冲突的风险,如果有应该在本分支先解决冲突然后在提交合并. 否则解决冲突的工作就全部转 ...

  5. CF #749

    A 题意 有个长度为n的序列, 每个数互不相同, 求总和最大的最长子序列, 并输出每个i: 题解 emmmmmm, 刚开始看到这个数据和题解被迷惑了, 以为有什么顺序, 并且一直在想一些复杂度较高的算 ...

  6. Oracle 19c 单机

    环境 vm虚拟机 双磁盘 操作系统 Oracle Linux 7.9 操作系统安装带图形 选择中文,注意不要新建用户 关闭防火墙 selinux 配置好IP 挂载系统盘镜像 修改主机名 配置hosts ...

  7. Redis网络库源码分析(1)之介绍篇

    一.前言 Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析.其实网上已经有非常多有关这个网络库 ...

  8. QuantumTunnel:协议路由 vs 端口路由

    本篇来聊一下内网穿透中流量转发的问题 内网穿透和核心逻辑是根据流量的路由信息准确地将公网流量路由到指定的机器端口上,从而完成一次流量的内网穿透. 这里有一个核心问题,路由信息从哪里获取? 常见的有将路 ...

  9. OpenXml SDK学习笔记(4):设置文件级别的样式

    观察上一段日记最后的代码: 这里的样式基本可以理解为行内CSS.那么既然有行内的样式,就肯定有外部的样式.那这部分就对应笔记1里说的style.xml文件.这个文件对应的是Document.MainD ...

  10. [JS]什么是闭包?

    首先来思考一下下面的案例: function unclosure() { let count = 0 return count++ } for (let index = 0; index < 1 ...