P2638

安全系统

这个题目描述是真的屑。

还有这个题为什么要转化为组合数学的问题啊。。。

完全可以直接无脑 DP 好不好。。。

定义 $f(i,j,k)$ 表示前 $n$ 个位置恰用了 $i$ 个 0 和 $j$ 个 1。

那么很显然 $f(i,j,k)=\sum_{p=0}^j\sum_{q=0}^kf(i-1,p,q)$。

很显然这个东西是可以用前缀和优化的。

于是乎我们甚至连 $f(i,j,k)$ 都不需要了。

直接根据 $c(i,j,k)=\sum_{p=0}^j\sum_{q=0}^kf(i,j,k)$ 来转移即可。

就是 $c(i,j,k)=c(i,j-1,k)+c(i,j,k-1)-c(i,j-1,k-1)+c(i-1,j,k)$。

最后的答案就是 $Ans=c(n,a,b)$。

初始化 $\forall i\in[0,a],j\in[0,b]$,都有 $c(0,i,j)=1$。

时间复杂度 $O(nab)$。

数组还可以用滚动数组优化。

对于这种思路的递推来讲,这个多项式复杂度估计是下限了,除非有更强的递推方式优化到线性递推,然后再矩阵快速幂什么的,不过我不会。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll __int128
using namespace std;

const ll N=1e2;

ll n,a,b;

ll c[N+5][N+5][N+5];

inline ll read() {
ll ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-f;ch=getchar();}
while(ch>='0'&&ch<='9') {ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*f;
}

void write(ll x) {
static char buf[22];static ll len=-1;
if(x>=0) {
do{buf[++len]=x%10+48;x/=10;}while(x);
}
else {
putchar('-');
do{buf[++len]=-(x%10)+48;x/=10;}while(x);
}
while(len>=0) putchar(buf[len--]);
}

int main() {

n=read();a=read();b=read();

for(ll i=0;i<=a;i++) {
for(ll j=0;j<=b;j++) {
c[0][i][j]=1;
}
}
for(ll i=1;i<=n;i++) {
for(ll j=0;j<=a;j++) {
for(ll k=0;k<=b;k++) {
if(j>=1) c[i][j][k]+=c[i][j-1][k];
if(k>=1) c[i][j][k]+=c[i][j][k-1];
if(j>=1&&k>=1) c[i][j][k]-=c[i][j-1][k-1];
c[i][j][k]+=c[i-1][j][k];
}
}
}

write(c[n][a][b]);

return 0;
}