1 条题解
-
0
自动搬运
来自洛谷,原作者为

fy0123
**搬运于
2025-08-24 21:43:32,当前版本为作者最后更新于2017-10-19 13:47:01,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这题是个dp。
定义方程f[i][j]表示从n~i,用了j个0的方案数。要n~i倒着处理是为之后的第k大做帮助。
第一问就直接dp,我们记录下p0[i],p1[i]分别表示从左往右第i个0的位置,第i个1的位置。转移的时候根据当前位放0还是放1,且是否满足距离小于等于d,计算即可。
然后对于第二问,我们这么做:
-
如果当前位置放0的方案数>=k,当前位就放0。
-
如果不能放0,就放1,并且k减去放0的方案数。
以上所有说的可以放0或放1都是建立在题目要求的前提下的,即距离不超过d
然后我们有一个问题了:如果方案数超过k的超过太多了怎么办?按照题意来说我们应该是要%10^8的,但是由于要求后面的第k大需要用到这个方案数,如果我们直接%10^8,就会导致结果不正确了。
对于这个问题我们再开一个g[i][j]也记录方案数,如果g[i][j]>10^8了就把它设成10^8+1,然后之后算第k大的时候就用g[i][j]当做方案数,这样就没事了。
下面放代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 2010; const int MOD = 1e8; int n, d, k, cnt0 = 0, cnt1 = 0; int p0[N], p1[N], f[N][N], g[N][N]; char s[N]; int main() { scanf("%d%d%d", &n, &d, &k); scanf("%s", s+1); for (int i = 1; i <= n; i ++) if (s[i] == '0') p0[++ cnt0] = i; else p1[++ cnt1] = i; f[n+1][0] = g[n+1][0] = 1; for (int i = n; i >= 1; i --) for (int j = 0; j <= min(n-i+1, cnt0); j ++){ int k = n-i+1-j; if (j && abs(p0[cnt0-j+1]-i) <= d){ f[i][j] += f[i+1][j-1]; if (f[i][j] > MOD) f[i][j] -= MOD; g[i][j] = min(g[i][j]+g[i+1][j-1], MOD+1); } if (k && abs(p1[cnt1-k+1]-i) <= d){ f[i][j] += f[i+1][j]; if (f[i][j] > MOD) f[i][j] -= MOD; g[i][j] = min(g[i][j]+g[i+1][j], MOD+1); } } printf("%d\n", f[1][cnt0]); int s0 = cnt0, s1 = cnt1; for (int i = 2; i <= n; i ++){ if (s0 && abs(p0[cnt0-s0+1]-i+1) <= d){ if (g[i][s0-1] >= k) s0 --, putchar('0'); else s1 --, k -= g[i][s0-1], putchar('1'); } else s1 --, putchar('1'); } if (s0) putchar('0'); else putchar('1'); return 0; } -
- 1
信息
- ID
- 1994
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者