题目大意

有 \(n\) 个白色的小球排成一排,有一个长为 \(k\) 的字符串 \(S\)。接下来进行 \(k\) 次操作。

第 \(i\) 个操作,选择一段连续的小球(可以为空),若 \(S\) 中第 \(i\) 个字符是 r,则将这些球染成红色;若是 b,则将它们染成蓝色。由于染料的特性,不能直接用蓝色来染白色。

求在进行完所有操作后,所有小球的颜色序列可以有多少种。

对 \(10^9+7\) 取模。

\(n,k\le 70\).

解题思路

首先,因为考虑按顺序进行染色操作的结果非常复杂,而且难以分辨重复状态,所以不妨考虑如何判断一个序列是否合法。

不妨假设整个序列有 \(m\) 个有色段,每段的蓝色段个数为 \(b_i\)。

显然对于 \(b_i=0\) 的段,只需要一个 r;而对于 \(b_i>1\) 的段,经过手玩不难发现,它需要一个 rb,以及 \(b_i-1\) 次任意染色。

所以假设我们已经有了 \(b\) 数组,不妨贪心地达成染色方案:

按照 \(b_i\) 的从大到小排序,首先尽量选最靠前的 r,然后对于 \(b_i>0\) 的,在选的 r 之后尽量选最靠前的 b,最后对于 \(b_i>1\) 的,选择 rb 后最靠前的 \(b_i-1\) 个操作。

因为 \(n\le 70\),可以用复杂度划分数相关的做法,那么不妨直接类似划分数枚举 \(b_i\) 的情况。

接下来对于已经确定的一组 \(b_i\) 如何求方案的思路,个人感觉比较妙 (从 xht37 那里学到的)。

以 \(b={1,2,2,3,4}\) 为例,首先是一个多重组合数 \(\binom{5}{1,2,1,1}\),然后是具体染色的方案数。

这个序列对应的染色序列一定包含子序列 R W B W B W BRB W BRBRB,也就是每个 \(b_i\) 的最短可能,长度为 \(15\)。

同时,其对应的染色序列一定是 W R W RBR W RBR W RBRBR W RBRBRBR W 的子序列,也就是每个 \(b_i\) 的最长可能,长度为 \(25\)。

而此时我们的染色序列是把相邻的同色球缩到一起,所以最终方案相当于把 \(n-15\) 个字符分成 \(25\) 组,每组可以为空的方案数。答案为 \(\binom{n-15+25-1}{25-1}\)。

#include <bits/stdc++.h>
#define lfor(x, y, z) for(int x(y), x##_end(z); x <= x##_end; ++x)
#define rfor(x, y, z) for(int x(y), x##_end(z); x >= x##_end; --x)
#define lep(x, y, z) for(int x(y), x##_end(z); x < x##_end; ++x)
#define ls(x) t[x].lc
#define rs(x) t[x].rc
#define LL long long
using namespace std; const int N(205), mod(1e9 + 7); int n, K; char ch[N];
int Ans;
int m, b[N];
int ban[N], loc[N];
int fac[N], ifac[N];
int div_cnt; template <typename T> inline void read(T &x){
x = 0; int f = 1, c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
x *= f;
} inline int Mod(int x){ return x + ((x >> 31) & mod); }
inline void MOD(int &x){ x = x + ((x >> 31) & mod); }
inline int C(int n, int m){
if(n < m || m < 0) return 0;
return (LL) fac[n] * ifac[n - m] % mod * ifac[m] % mod;
}
inline int qpow(int x, int a = mod - 2){
int sum = 1;
while(a) sum = a & 1 ? (LL) sum * x % mod : sum, x = (LL) x * x % mod, a >>= 1;
return sum;
} void prep(int n = 200){
fac[0] = 1; lfor(i, 1, n) fac[i] = (LL) fac[i - 1] * i % mod;
ifac[n] = qpow(fac[n]); rfor(i, n - 1, 0) ifac[i] = (LL) ifac[i + 1] * (i + 1) % mod;
} bool legal(){
int now = 1, sum = m - 1; lfor(i, 1, K) ban[i] = 0;
lfor(i, 1, m) if(b[i]) sum += b[i] * 2 - 1; if(sum > n) return 0; rfor(i, m, 1){
while(now <= K && ch[now] != 'r') ++now;
if(now > K || ch[now] != 'r') return 0; loc[i] = now, ban[now] = 1, ++now;
} now = 1;
rfor(i, m, 1) if(b[i]){
now = max(now, loc[i] + 1);
while(now <= K && ch[now] != 'b') ++now;
if(now > K || ch[now] != 'b') return 0; loc[i] = now, ban[now] = 1, ++now;
} now = 1;
rfor(i, m, 1) if(b[i]){
int sum = b[i] - 1;
now = max(now, loc[i] + 1);
while(now <= K && sum) sum -= !ban[now], ++now;
if(sum) return 0;
}
return 1;
} inline int Calc(){
int sum = fac[m], p = 1;
lfor(i, 2, m) if(b[i] != b[p]) sum = (LL) sum * ifac[i - p] % mod, p = i;
return (LL) sum * ifac[m - p + 1] % mod;
}
void check(){
if(!legal()) return;
int A = m - 1, B = m + 1;
lfor(i, 1, m){
if(b[i] == 0) A += 1, B += 1;
else A += b[i] * 2 - 1, B += b[i] * 2 + 1;
}
int tmp = (LL) Calc() * C(n - A + B - 1, B - 1) % mod;
MOD(Ans += (LL) Calc() * C(n - A + B - 1, B - 1) % mod - mod);
}
void dfs(int R, int A, int L, int lst){
check();
++m;
for(int i = lst; R && i + 1 <= A && L + max(1, i * 2 - 1) <= n + 1; ++i)
b[m] = i, dfs(R - 1, A - 1 - i, L + max(1, i * 2 - 1), i);
--m;
} int main(){
read(n), read(K), prep();
scanf("%s", ch + 1); int cntr = 0, cntb = 0;
lfor(i, 1, K) if(ch[i] == 'r') ++cntr; else ++cntb; dfs(cntr, K + 1, 0, 0); cout << Ans << endl;
return 0;
}

[题解][ARC089D] ColoringBalls的更多相关文章

  1. 贪心/构造/DP 杂题选做Ⅲ

    颓!颓!颓!(bushi 前传: 贪心/构造/DP 杂题选做 贪心/构造/DP 杂题选做Ⅱ 51. CF758E Broken Tree 讲个笑话,这道题是 11.3 模拟赛的 T2,模拟赛里那道题的 ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

随机推荐

  1. Python使用pip安装No matching distribution found for PyYaml==5.3.1

    ERROR: Command errored out with exit status 1: command: /usr/local/dmahz/p_book_data/bin/python3.9 - ...

  2. List、Set、Map详解及区别

    一.List接口 List是一个继承于Collection的接口,即List是集合中的一种.List是有序的队列,List中的每一个元素都有一个索引:第一个元素的索引值是0,往后的元素的索引值依次+1 ...

  3. java程序如何确保多线程的运行安全?

    线程的安全问题体现在: 原子性:一个或多个操作在CPU执行过程中不被中断的特性 可见性:一个线程对共享变量的修改,另一个线程能立刻看到 有序性:程序执行的顺序按照代码的先后顺序执行 导致线程存在安全问 ...

  4. java-jdbc-all

    jdbc相关解析 JDBC(Java DataBase Connectivity,Java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语 ...

  5. 在java web工程中jsp页面中使用kindeditor

    在这之前我们用Notepad++写过kindeditor 在Java web工程里也差不多 首先我们复制之前的thml代码粘贴到工程里 然后把样式也复制进去 然后就可以运行了

  6. 用maven建立一个工程5

    在命令行里面输入cd myapp再按回车 再输入mvn compile再按回车 再输入 cd target按回车 再输入cd../按回车 再输入mvn package按回车 最后输入java -cla ...

  7. 学习笔记 - Sass的安装与使用手册

    最近因为工作需要,自学了Sass.现在将学习笔记整理在这里,供大家参考. 1. Sass的安装 Sass的编辑器安装方法有很多,大致能分为两种:应用程序(application)和命令行界面(comm ...

  8. html5不熟悉的标签全称

    <dl></dl> 定义列表(英文全称:DefinitionList) <dt> 放在每个定义术语词前(定义术语.英文全称:DefinitionTerm) 名称 & ...

  9. 要多简单就有多简单的H5拍照加水印

    来一个简单粗暴的gif演示图 先来html 内容 <video id="video" width="320" height="240" ...

  10. Leetcode1/242/383-HashMap常用方法以及遍历排序方式

    HashMap常用方法以及遍历排序方式 常用方法 map.containsKey() map.put() map1.equals(map2) 遍历方式 Iterator<Map.Entry< ...