AT2161 [ARC065D] シャッフル / Shuffling
https://www.luogu.com.cn/problem/AT2161
考虑DP
设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示考虑前
i
i
i位,放了
j
j
j个
1
1
1的方案数
那么可以轻松得到转移方程
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
1
]
f[i][j]=f[i-1][j]+f[i-1][j-1]
f[i][j]=f[i−1][j]+f[i−1][j−1]
即这一位是否放
1
1
1
但是有些状态是非法的,所以要对于每个
i
i
i吧合法的转移区间先提前算出来再DP
code:
#include<bits/stdc++.h>
#define N 3005
#define mod 1000000007
using namespace std;
int n, m, f[N][N], s[N], nxt[N];
char st[N];
int main() {
scanf("%d%d", &n, &m);
scanf("%s", st + 1);
for(int i = 1; i <= n; i ++) s[i] = s[i - 1] + st[i] - '0', nxt[i] = i;
while(m --) {
int l, r;
scanf("%d%d", &l, &r);
nxt[l] = max(nxt[l], r);
}
for(int i = 1; i <= n; i ++) nxt[i] = max(nxt[i - 1], nxt[i]);
// for(int i = 1; i <= n; i ++) printf("%d ", nxt[i]); printf("\n");
f[0][0] = 1;
for(int i = 1; i <= n; i ++) {
int r = min(i, s[nxt[i]]);
int l = max(0, i - (nxt[i] - s[nxt[i]]));
for(int j = l; j <= r; j ++) {
f[i][j] = (f[i][j] + f[i - 1][j]) % mod;
if(j) f[i][j] = (f[i][j] + f[i - 1][j - 1]) % mod;
}
// for(int j = 0; j <= n; j ++) printf("%d ", f[i][j]); printf(" %d %d\n", l, r);
}
printf("%d", f[n][s[n]]);
return 0;
}