卧薪尝胆,厚积薄发。
NOI2017 泳池
Date: Fri Mar 01 14:47:09 CST 2019
In Category:
NoCategory
Description:
高为
$1001$
宽为
$n$
的长方形网格,每个位置有
$p$
的概率为
$0$
,要求选出的全零矩形与下边界相连,问能选出的最大矩形面积恰好为
$k$
的概率。
$1\leqslant n\leqslant 10^9,1\leqslant k\leqslant 1000$
Solution:
注意到虽然
$n$
非常大,但是
$k$
不是特别大,因此我们可以设计一个复杂度只与
$k$
有关的辅助
$DP$
来预处理一些东西,我们可以设计这样一个
$DP$
,设
$dp[i][j]$
表示底部宽为
$i$
(高为
$1001$
),在底下
$j$
行都是白色,在第
$j+1$
行有至少一个黑色块,并且所有和底面相连的白色矩形面积都不超过
$k$
的概率,转移的话可以枚举高度在
$j+1$
处的第一个矩形,那么这个黑色块的前面高度必须大于
$j$
,后面至少大于等于
$j$
:
$$
dp[i][j]=[i\times j\leqslant k](1-p)p^{j-1}\sum_{x=1}^i\Bigl((\sum_{j'>j}dp[x-1][j'])\times(\sum_{j'\geqslant j}dp[i-x][j'])\Bigr)
$$
只要把
$dp$
数组后缀和优化一下,也就是:
$$
\begin{align}
dp'[i][j]&=\sum_{j'\geqslant j}dp[i][j]\\
dp[i][j]&=[i\times j\leqslant k](1-p)p^j\sum_{x=1}^i(dp'[x-1][j+1]\times dp'[i-x][j])
\end{align}
$$
前面那个概率表示前
$j$
行都是
$0$
(
$p^j$
)第
$j+1$
行为
$1$
(
$1-p$
),后面就是枚举第
$j+1$
行第一个黑色块的位置。
这个转移是
$O(k^2)$
的,因为第一维个数为
$k$
,第二维个数为
$k/i$
,第三维为
$i$
。
然后设
$f[n]$
表示宽为
$n$
时候的答案,我们枚举最下面最后一段白色的长度,也就是最下一行最后一个黑色块的位置,可以得到转移:
$$
f[n]=\sum_{i=0}^kf[n-i-1]\times dp'[i][1]\times (1-p)=\sum_{i=1}^{k+1}f[n-i]\times dp'[i-1][1]\times (1-p)
$$
发现这就是一个常系数齐次线性递推,于是用多项式取模优化即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,k,x,y;
#define MAXK 2010
int dp[MAXK][MAXK],dp_[MAXK][MAXK];
int p;
#define MOD 998244353
int power(int a,int b)
{
int res = 1;
while(b > 0)
{
if(b & 1)res = 1ll * res * a % MOD;
a = 1ll * a * a % MOD;
b = b >> 1;
}
return res;
}
#define MAXN 10000001
int f[MAXN];
int g[MAXK];
int K;
struct matrix
{
int a[MAXK];
matrix(){memset(a,0,sizeof(a));}
int& operator [](int x){return a[x];}
int operator [](int x)const{return a[x];}
friend matrix operator * (matrix a,matrix b)
{
matrix res;
for(int i = 0;i < K;++i)
for(int j = 0;j < K;++j)
(res[i + j] += 1ll * a[i] * b[j] % MOD) %= MOD;
for(int i = 2 * K - 2;i >= K;res[i--] = 0)
for(int j = 1;j <= K;++j)
(res[i - j] += 1ll * res[i] * g[j] % MOD) %= MOD;
return res;
}
};
matrix power(matrix a,int b)
{
matrix res;res[0] = 1;
while(b > 0)
{
if(b & 1)res = res * a;
a = a * a;
b = b >> 1;
}
return res;
}
int solve(int k)
{
memset(dp,0,sizeof(dp));
memset(dp_,0,sizeof(dp_));
for(int j = 0;j <= k + 1;++j)dp_[0][j] = 1;
for(int i = 1;i <= k + 1;++i)
{
for(int j = 0;i * j <= k;++j)
{
int sum = 0;
for(int x = 1;x <= i;++x)
{
sum = (sum + 1ll * dp_[x - 1][j + 1] * dp_[i - x][j] % MOD) % MOD;
}
dp[i][j] = 1ll * (1 - p + MOD) * power(p,j) % MOD * sum % MOD;
for(int j_ = 0;j_ <= j;++j_)dp_[i][j_] = (dp_[i][j_] + dp[i][j]) % MOD;
}
}
++k;K = k;
for(int i = 0;i < k;++i)f[i] = dp_[i][0];
for(int i = 1;i <= k;++i)g[i] = 1ll * dp_[i - 1][1] % MOD * (1 - p + MOD) % MOD;
/*for(int x = k;x <= n;++x)
{
f[x] = 0;
for(int i = 1;i <= k;++i)
{
f[x] = (f[x] + 1ll * f[x - i] * g[i] % MOD) % MOD;
}
}*/
matrix m;m[1] = 1;
m = power(m,n);
int ans = 0;
for(int i = 0;i < k;++i)(ans += 1ll * m[i] * f[i] % MOD) %= MOD;
return ans;
}
int main()
{
scanf("%d%d%d%d",&n,&k,&x,&y);
p = 1ll * x * power(y,MOD - 2) % MOD;
cout << (solve(k) - solve(k - 1) + MOD) % MOD;
return 0;
}
In tag:
数学-多项式-常系数齐次线性递推
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡