惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
J
Java Code Geeks
L
LangChain Blog
V
V2EX
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
博客园 - 司徒正美
B
Blog
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog

某岛

AtCoder Beginner Contest 409 Luogu P5325. 【模板】Min_25 筛 UOJ #188. 【UR #13】Sanrd AtCoder Beginner Contest 371 AtCoder Beginner Contest 369 RPGMaker 2k3 百科 OneShot 的考古 2024“开创拓芯”游戏创享节的相关记录 CJ 回来后的戒断反应 Luogu P10221. [省选联考 2024] 重塑时光 Luogu P5308 [COCI2018-2019#4] Akvizna wqs 二分 歌唱王国 Lean 相关 BZOJ 3153. Sone1 The 2023 ICPC World Finals Luxor 新巴别塔 Sora 的想象与思考 Facebook Hacker Cup 2023 Round 1 AtCoder Beginner Contest 322 LLaMA 2 相关 HuggingFace AI Game Jam ACL 2023 Trans 相关… Luogu P2053. [SCOI2007] 修车 Luogu P1973. [NOI2011] NOI 嘉年华 Luogu P1933. [NOI2010] 旅行路线 Luogu P1954. [NOI2010] 航空管制 Luogu P2048. [NOI2010] 超级钢琴 Luogu P2046. [NOI2010] 海拔
Codeforces Round #844
2023-01-17 · via 某岛

又是久违的 tourist round。。。

Problem D.

Problem F – Bracket Insertion

题意

构造一个初始为空的括号序列,执行 n 次插入操作,每次操作任选序列中的一个位置,插入 "()"")("
问最终构成的括号序列为合法括号序列的操作方案的概率。

题解

组合计数。
先转换成计数问题,不难发现,对于第 i 次操作,我们有 i*2-1 种插入的位置可供选择,因而求出合法的操作方案总数,处以 1*3*5*...*2n-1 即可。
麻烦的是,我们甚至不能把每次插入的一对括号看成是一个独立的元素,后续插入操作甚至可以从中间破坏它。

回忆 Catalan 序列的做法,虽然和这个题看起来有很大的不同,前者是计数合法的括号序列总数,本题是计数合法的操作方案。
但是依然可以给我们提供许多有益的思路。回忆 Catalan 序列的卷积形递推公式。
我们实际上是考察第一组匹配的括号,然后分别枚举括号内或括号外的括号层数,以进行递归。

这个题是否可以使用同样的策略呢?
我们同样固定第一组括号,那么之后的括号,只会是下述三种情况之一:L(M)R。
因为被初始括号所分割,这里 L M R 的状态都独立的。
这里 L 和 R 的 size 可以合并在一起,这样我们只要枚举 M 的 size 即可。

这样我们就有了类 Catalan 的递推关系,进一步这里的不同时,子状态中并不一定要求是合法的括号序列,
我们需要记录子状态的不合法的等级,也就是前缀和的最小值。

进一步考虑细节,在转移时,我们发现我们只需要考虑前缀和 >= j 的状态,因此可以直接将前缀和定义在状态中。
f[i][j]: 执行了 i 次插入操作,且当前前缀和最小值不小于 -j 的方案数。
我们枚举左右的子树 size 和 a 或中间的子树 size b,得到转移方程:

 f[i][j] = \sum f_2[a][j] (p \cdot f[b][j+1]+q\cdot f[b][j-1]) \binom{i-1}{a, b}

这里的转移出现了组合数,一个常见的技巧是,我们可以类比 EFG 中的处理方法,对每个状态 f[i][j] 都除以 i! 以简化转移。(然后下标里有 -1 也很烦,不如也 offset 一下。。。)

https://codeforces.com/contest/1782/submission/189455837

#include <lastweapon/number>
using namespace lastweapon;
const int N = int(5e2) + 9;
Int f[N][N], f2[N][N], I[2*N], Fact[2*N]; // i step, min pref sum >= -(j-1)
int n; Int p, q;

int main() {

#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("/Users/minakokojima/Documents/GitHub/ACM-Training/Workspace/out.txt", "w", stdout);
#endif

    MOD = 998244353;
	RD(n); p = Int(RD()) / 10000; q = Int(1) - p;
	for (int i=(I[1]=1)+1; i<=2*n; i++) I[i] = 1ll * (MOD - MOD / i) * I[MOD%i] % MOD;
	Fact[0] = 1; REP_1(i, n) Fact[i] = Fact[i-1] * i;

	REP_1(i, n+1) f2[0][i] = f[0][i] = 1;

	// \sum f[a1] * f[a2] * (p*f[b]+q*f[b]) * \binom{i-1}{a, b}
	REP_1(i, n) REP_1(j, n-i+1) {
        REP(a, i) {
            int b = i-1-a;
            f[i][j] += f2[a][j] * (p*f[b][j+1] + q*f[b][j-1]);
        }
        f[i][j] *= I[i];
        REP(a, i+1) {
            int b = i-a;
            f2[i][j] += f[a][j] * f[b][j];
        }
	}

    Int z = f[n][1] * Fact[n]; REP_1(i, n) {
        z *= I[i*2-1];
    }
    cout << z << endl;
}

进一步,和 Catalan 序列一样,这个题也可以从树的角度进行思考。
我们设法建立起括号序列与一个有标号森林的一一对应关系(森林也可视为有一个虚根节点的树),
每次操作,我们都相当于按照顺序,插入一个新的有标号的叶子节点(区分孩子的顺序)(祖先的标号一定小于孩子)。

括号的状态对应节点处权值 (1 或 -1),括号序列的前缀和对应树中的一段 dfs 序,也就是根到每一个节点的路径的权值和。
括号序列合法,就是所有这些和都 >= 0

同样的,在转移过程中,括号序列允许不合法,我们进行同样的加维,只是这里因为我们已经建立了一一对应关系,转移时我们可以直接对这里的树结构进行计数。
我们直接枚举树的形态,再用组合数重新进行标号,得到更简单的转移方程:
 f[i][j] = \sum f[a][j] (p\cdot f[b][j+1]+q\cdot f[b][j-1]) \binom{i}{a, b+1}

注意这里的 b+1 是因为,我们已经把 size 为 b 的森林,和一个新的节点作为了一个完整的子树,连接在虚根节点上。
得到的方程更为简洁。

(当然这两种做法是完全等价的,无论从括号序列的角度分析还是从树的角度分析,只是描述的语言略有不同罢了,都能得到两种转移方程,
区别是我们递归的时候是拎出来考察的是编号为 1 的节点所在的子树,还是位置靠边的一个子树。)

https://codeforces.com/contest/1782/submission/189456196

#include <lastweapon/number>
using namespace lastweapon;
const int N = int(5e2) + 9;
Int f[N][N], I[2*N], Fact[2*N]; // i step, min pref sum >= -(j-1)
int n; Int p, q;

int main() {

#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("/Users/minakokojima/Documents/GitHub/ACM-Training/Workspace/out.txt", "w", stdout);
#endif

    MOD = 998244353;
	RD(n); p = Int(RD()) / 10000; q = Int(1) - p;
	for (int i=(I[1]=1)+1; i<=2*n; i++) I[i] = 1ll * (MOD - MOD / i) * I[MOD%i] % MOD;
	Fact[0] = 1; REP_1(i, n) Fact[i] = Fact[i-1] * i;

	REP_1(i, n+1) f[0][i] = 1;

	// \sum f[a] * (p*f[b]+q*f[b]) * \binom{i}{a, b+1}
	REP_1(i, n) REP_1(j, n-i+1) {
        REP(a, i) {
            int b = i-1-a;
            f[i][j] += f[a][j] * (p*f[b][j+1] + q*f[b][j-1]) * I[b+1];
        }
	}

    Int z = f[n][1] * Fact[n]; REP_1(i, n) {
        z *= I[i*2-1];
    }
    cout << z << endl;
}

Posted by xiaodao
Category: 日常