Atcoder 题面传送门 & 洛谷题面传送门

神仙题。

在下文中,方便起见,用 R/B 表示颜色序列中球的颜色,用 r/b 表示染色序列中将连续的区间染成的颜色。

首先碰到这一类计算有多少个可以被得到的序列的问题,我们为了避免计算重复,肯定会先考虑什么样的序列能够被得到,从而设计一个 DP 状态。此题也不例外。考虑对于一个颜色序列它能否能够被得到,显然对于这个颜色序列中白色格子,它们肯定从来没有被覆盖,因此我们考虑这些白色格子被分割而成的连续段。我们将这些连续段分为两类:不含蓝色格子和含蓝色格子。对于前者,显然一个 r 即可搞定。后者稍微有点复杂,我们来分析下什么样的序列能够染出它,显然同色连续段可以缩在一起,而如果开头是蓝色格子,那我们肯定需要先在它上面染一遍 r,因此我们不妨在它前面补一个红色格子,对于结尾的格子也同理,这样我们只用考虑 RBRBRB…BR 能够被什么样的染色序列得到即可。手玩一下 RBRBRBR 的染色方案可以发现,RBRBRBR 能够被以下四个长度为 \(4\) 的序列得到:

  • rbrr
  • rbrb
  • rbbr
  • rbbb

我们可以很明显地发现,能够得到 RBRBRBR 的染色序列的头两个元素都是 rb,而后两个元素是什么都无所谓,并且显然没有比这个长度更短的序列了,而对于更长的序列,它们肯定包含子序列 rb,并且 rb 以后都至少有 \(2\) 个元素,因此它们肯定是没有这四个长度为 \(4\) 的序列来得更优的,因此我们不妨归纳一下:对于有 \(B\) 个蓝色连续段的杂色序列,有用的序列长度都是 \(B+1\),并且头两个元素都是 rb,至于后面的元素,则无所谓。

因此我们假设分出来的杂色连续段有 \(x\)​ 个,它们当中蓝色格子分别有 \(c_1,c_2,\cdots,c_x\) 个,而只含红色的连续段有 \(y\) 个,那么这样的染色序列能够被染出来当且仅当 \(S\) 能够被拆分成 \(x+y\) 个子序列(不必全部字符都属于某个子序列,但每个字符最多属于一个子序列),其中 \(y\) 个恰好是 r,而对于另外 \(x\) 个,都以 rb 开头,并且第 \(i\) 个长度恰为 \(c_i+1\)。

这样我们可以贪心地判定一个序列是否可以被染出:我们先贪心地选出最靠前的 \(x\) 个 rb,再从剩下的子序列中贪心地选出 \(y\) 个 r,然后我们对于此时的局面,对每个 \(i\) 求出 \([i,n]\) 中有多少个格子目前没有被染色,设为 \(sum_i\),然后我们将选出的 \(x\) 个 rbb 的位置从小到大排序,设为 \(ed_1,ed_2,\cdots,ed_x\),再将 \(c\) 数组从大到小排序,即不妨假设 \(c_i\ge c_{i+1}\),这样我们就贪心地将第一个 rb 分给 \(c\) 最大的连续段,第二个 rb 分给 \(c\) 第二大的,以此类推,这样我们可知一段序列符合要求,当且仅当 \(\forall i,sum_{ed_i}\ge\sum\limits_{j=i}^x(c_j-1)\)。

我们考虑枚举 \(x,y\)​,然后按照上面的方式先贪心地选出 \(x\)​ 个 rb 和 \(y\)​ 个 r,这样我们可以知道 \(sum_i\)​ 及 \(ed_i\)​。然后我们考虑倒着 DP,设 \(dp_{i,j,k}\)​ 表示目前钦定了 \(i\sim x\)​ 的段中蓝色点的个数,目前 \(\sum\limits_{j=i}^xc_j-1=j,c_i=k\)​,有多少种杂色段之间两两之间位置关系的可能,转移就枚举 \(c_j=k\)​ 的 \(j\)​ 有多少个,设为 \(p\)​,那么转移就类似于一个二项加法卷积,即 \(dp_{i,j,k}=\sum\limits_{p}\sum\limits_{l}dp_{i+p,j-(k-1)p,l}·\dbinom{x-i+1}{p}[l<k]\)​,注意到 \(p\)​ 的枚举是调和级数,因此 \(p\)​ 枚举的总复杂度是 \(\ln n\)​ 级别的,而 \(l\)​ 的枚举可以前缀和优化掉,这样单次 DP 就达到了 \(n^3\ln n\)​​。

接下来考虑怎样计算一次 DP 对答案的贡献,即如何用 \(dp_{1,j,k}\)​ 即 \(x,y\)​ 的值计算答案,首先段之间的位置关系,\(x\)​ 个杂色段之间的位置关系已经在 DP 过程中钦定了,而 \(y\)​ 个杂色段与 \(x\)​ 个杂色段之间的位置关系就做个组合数,即 \(\dbinom{x+y}{x}\)​,接下来考虑怎样计算每个连续段中球的数量有多少种可能,我们先假设每个连续段为一个“盒子”,每个盒子中必须装同一颜色的球,那么先不妨假设对于一个由 \(c\)​ 个 B 构成的杂色连续段,它只有 BRBRBRBR…B,那么这样有 \(2c-1\)​ 个盒子,每个盒子中都必须至少有一个球,而两边还可以放一些 R,因此还会产生两个可空的盒子,对于每个纯色连续段,它肯定恰好会产生一个必须放球的盒子,而连续段与连续段之间的 \(x+y-1\)​ 个空隙,它们必须放黑球,有 \(x+y-1\)​ 个必须放球的盒子,而两边的黑球盒子可以空,因此还有两个可以为空的盒子,这样必须放球的盒子数 \(A=2j+2x+2y-1\)​,可以为空的盒子数 \(B=2x+2\)​,组合数算贡献即可。

时间复杂度 \(n^5\ln n\),由于常数很小,可以通过。

启示:

  • 对于求解有多少个序列能够被得到的这一类问题,不妨先考虑一下什么样的序列能够被得到,而如果没有推出足够的性质(比如说如果每次操作的啥啥啥不同,最终得到的序列一定不同)之前,最好不要直接从操作序列的时间轴入手 DP。

  • 如果一些信息在 DP 的过程中不能直接求得/计算 DP 的系数时需要用到这些信息,那不妨先枚举它们,这样 DP 的时候系数就比较好求了。

P.S.:如果你下面程序的最后一组数据输出 2088,看看是不是多算了 BRB黑BRB 这一不合法的序列。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a, 0, sizeof(a))
#define fill1(a) memset(a, -1, sizeof(a))
#define fillbig(a) memset(a, 63, sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
#define mt make_tuple
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
template <typename T1, typename T2> void chkmin(T1 &x, T2 y){
if (x > y) x = y;
}
template <typename T1, typename T2> void chkmax(T1 &x, T2 y){
if (x < y) x = y;
}
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long double ld;
namespace fastio {
#define FILE_SIZE 1 << 23
char rbuf[FILE_SIZE], *p1 = rbuf, *p2 = rbuf, wbuf[FILE_SIZE], *p3 = wbuf;
inline char getc() {
return p1 == p2 && (p2 = (p1 = rbuf) + fread(rbuf, 1, FILE_SIZE, stdin), p1 == p2) ? -1: *p1++;
}
inline void putc(char x) {(*p3++ = x);}
template <typename T> void read(T &x) {
x = 0; char c = getchar(); T neg = 0;
while (!isdigit(c)) neg |= !(c ^ '-'), c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (neg) x = (~x) + 1;
}
template <typename T> void recursive_print(T x) {
if (!x) return;
recursive_print (x / 10);
putc (x % 10 ^ 48);
}
template <typename T> void print(T x) {
if (!x) putc('0');
if (x<0) putc('-'), x = -x;
recursive_print(x);
}
template <typename T> void print(T x,char c) {print(x); putc(c);}
void print_final() {fwrite(wbuf, 1, p3-wbuf, stdout);}
}
const int MAXN = 70;
const int MAXC = 500;
const int MOD = 1e9 + 7;
int n, k, c[MAXC + 5][MAXC + 5]; char s[MAXN + 5];
bool vis[MAXN + 5]; int ed[MAXN + 5], suf[MAXN + 5], sum[MAXN + 5];
bool contribute(int x, int y) {
memset(vis, 0, sizeof(vis));
int c1 = 0, c2 = 0;
for (int i = 1; i <= k; i++) {
if (s[i] == 'r') {
if (c1 < x + y) c1++, vis[i] = 1;
} else {
if (c2 < x && c1 > c2) c2++, vis[i] = 1, ed[c2] = i;
}
}
if (c1 != x + y || c2 != x) return 0;
suf[k + 1] = 0;
for (int i = k; i; i--) suf[i] = suf[i + 1] + (!vis[i]);
memset(sum, 0, sizeof(sum));
for (int i = 1; i <= x; i++) sum[i] = suf[ed[i]];
return 1;
}
int dp[MAXN + 5][MAXN + 5][MAXN + 5], sdp[MAXN + 5][MAXN + 5][MAXN + 5];
void calc_dp(int x, int y) {
memset(sdp, 0, sizeof(sdp)); memset(dp, 0, sizeof(dp));
dp[x + 1][0][0] = 1;
for (int i = 0; i <= n; i++) sdp[x + 1][0][i] = 1;
for (int i = x; i; i--) {
for (int j = 0; j <= sum[i]; j++) {
for (int l = 1; l <= n; l++) {
for (int p = 1; p * (l - 1) <= j && p <= x - i + 1 && j - (l - 1) * p <= sum[i + p]; p++) {
dp[i][j][l] = (dp[i][j][l] + 1ll * c[x - i + 1][p] * sdp[i + p][j - (l - 1) * p][l - 1]) % MOD;
}
sdp[i][j][l] = (sdp[i][j][l - 1] + dp[i][j][l]) % MOD;
}
}
}
}
int main() {
// freopen("color.in", "r", stdin);
// freopen("color.out", "w", stdout);
scanf("%d%d%s", &n, &k, s + 1);
for (int i = 0; i <= MAXC; i++) {
c[i][0] = 1;
for (int j = 1; j <= i; j++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
}
}
int res = 0;
for (int x = 0; x <= n; x++) {// x sequences with at least 1 blue ball
for (int y = 0; x + y <= n; y++) {// y sequences with only red balls
if (!x && !y) continue;
if (!contribute(x, y)) continue;
// printf("x = %d; y = %d:\n", x, y);
// for (int i = 1; i <= k; i++) printf("%d ", vis[i]);
// printf("\n");
// for (int i = 1; i <= x; i++) printf("%d ", sum[i]);
// printf("\n");
calc_dp(x, y);
for (int s = 0; s <= sum[1]; s++) {
for (int lst = 0; lst <= n; lst++) if (dp[1][s][lst]) {
int A = (2 * s + x) + (x + y - 1) + y, B = 2 * x + 2;
// printf("!! %d %d %d %d %d %d\n", s, lst, dp[1][s][lst], A, B,
// 1ll * dp[1][s][lst] * c[x + y][y] % MOD * c[n + B - 1][A + B - 1] % MOD);
res = (res + 1ll * dp[1][s][lst] * c[x + y][y] % MOD * c[n + B - 1][A + B - 1]) % MOD;
}
}
}
}
printf("%d\n", (res + 1) % MOD);
return 0;
}
/*
4 3
rrb 7 4
rbrb 7 6
rbrrrb
*/

Atcoder Regular Contest 089 D - ColoringBalls(DP)的更多相关文章

  1. Atcoder Grand Contest 033 D - Complexity(dp)

    Atcoder 题面传送门 & 洛谷题面传送门 首先 \(n^5\) 的暴力非常容易想,设 \(dp_{a,b,c,d}\) 表示以 \((a,b)\) 为左上角,\((c,d)\) 为右下角 ...

  2. AtCoder Grand Contest 031 B - Reversi(DP)

    B - Reversi 题目链接:https://atcoder.jp/contests/agc031/tasks/agc031_b 题意: 给出n个数,然后现在你可以对一段区间修改成相同的值,前提是 ...

  3. Atcoder Regular Contst 084 D - XorShift(bitset)

    洛谷题面传送门 & Atcoder 题面传送门 没错,这就是 Small Multiple 那场的 F,显然这种思维题对我来说都是不可做题/cg/cg/cg 首先如果我们把每个二进制数看作一个 ...

  4. Atcoder Grand Contest 021 F - Trinity(dp+NTT)

    Atcoder 题面传送门 & 洛谷题面传送门 首先我们考虑设 \(dp_{i,j}\) 表示对于一个 \(i\times j\) 的网格,其每行都至少有一个黑格的合法的三元组 \((A,B, ...

  5. Atcoder Regular Contest 125 E - Snack(最小割转化+贪心)

    Preface: 这是生平第一道现场 AC 的 arc E,也生平第一次经历了 performance \(\ge 2800\)​,甚至还生平第一次被 hb 拉到会议里讲题,讲的就是这个题,然鹅比较尬 ...

  6. AtCoder Regular Contest 089

    这场一边吃饭一边打,确实还是很菜的 C - Traveling Time limit : 2sec / Memory limit : 256MB Score : 300 points Problem ...

  7. AtCoder Grand Contest 032-B - Balanced Neighbors (构造)

    Time Limit: 2 sec / Memory Limit: 1024 MB Score : 700700 points Problem Statement You are given an i ...

  8. 2019 Multi-University Training Contest 1 A.Blank(dp)

    题意:现在要你构造一个只有{0,1,2,3} 长度为n且有m个限制条件的序列 问你方案数 思路:dp[i][j][k][now]分别表示四个数最后出现的位置 最后可以滚动数组 优化一下空间 ps:我的 ...

  9. AtCoder Regular Contest 093

    AtCoder Regular Contest 093 C - Traveling Plan 题意: 给定n个点,求出删去i号点时,按顺序从起点到一号点走到n号点最后回到起点所走的路程是多少. \(n ...

随机推荐

  1. 初始CSS01

    CSS基础知识 CSS介绍 CSS全称为层叠样式表,与HTML相辅相成,实现网页的排版布局与样式美化. 使用方式 根据样式表在页面中呈现的方式不同,可以通过以下三种方式在页面中使用格式 内联样式 改样 ...

  2. .Net Core中使用ElasticSearch(一)

    一.安装配置 在官网下载Es,注意版本号,不同大版本号之间差异很大.我安装的是7.14.0版本 1.1 安装成服务 cmd 进入bin目录下执行 elasticsearch-service.bat i ...

  3. 【二食堂】Beta - 事后分析

    事后分析 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? Beta阶段我们首先要对文本标注方式进行优化,其次时添加好友系统,实现邀请好友共同标注的功能. ...

  4. Scrum Meeting 0505

    零.说明 日期:2021-5-5 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成邮箱注册页面功 ...

  5. pwn200,一道不完全考察ret2libc的小小pwn题

    pwn200 ---XDCTF-2015 每日一pwn,今天又做了一个pwn,那个pwn呢???攻防世界的进阶区里的一道小pwn题,虽然这个题考察的知识不多,rop链也比较好构建,但是还是让我又学到了 ...

  6. 2021.10.12考试总结[NOIP模拟75]

    T1 如何优雅的送分 考虑式子的实际意义.\(2^{f_n}\)实际上就是枚举\(n\)质因子的子集.令\(k\)为这个子集中数的乘积,就可以将式子转化为枚举\(k\),计算\(k\)的贡献. 不难得 ...

  7. Spring:所有依赖项注入的类型

    一.前言 Spring文档严格只定义了两种类型的注入:构造函数注入和setter注入.但是,还有更多的方式来注入依赖项,例如字段注入,查找方法注入.下面主要是讲使用Spring框架时可能发生的类型. ...

  8. P2472 [SCOI2007]蜥蜴(最大流)

    P2472 [SCOI2007]蜥蜴 自己第一道独立做题且一遍AC的网络流题纪念... 看到这道题我就想到网络流建图的方式了... 首先根据每个高度,我们将每个点拆成两个点限流.之后根据跳的最大距离, ...

  9. cf16C Monitor(额,,,,水数学,,)

    题意: 一块镜子长宽是a*b.现在要调整(切割)成x:y的比例. 问调整完的最大面积是多少. 思路: 先将x,y弄成最简比例,然后放大到不超过min(a,b)即可. 代码: ll a,b,x,y; l ...

  10. scrapy 的response 的相关属性

    Scrapy中response介绍.属性以及内容提取   解析response parse()方法的参数 response 是start_urls里面的链接爬取后的结果.所以在parse()方法中,我 ...