模拟赛搬的题,dp 思路很明显,但难点就在于找到要转移的点在哪。

暴力

首先我们可以先考虑 \(k=1\) 的情况,这应该很好想,就是对于每一个右括号,找到其匹配的左括号,然后进行转移即可,这个过程可以用栈维护。

\(dp[i]\) 定义为以 \(i\) 为结尾的合法序列个数。假设当前右括号在 \(i\) 处,匹配的左括号在 \(j\) 处,则:

\[dp[i]=dp[j-1]+1
\]

注意一定是要在保证能找到的情况下,转移离自己最近的左括号,才能保证所有括号序列都被统计到了。

最后扫一遍把所有的 \(dp[i]\) 累加即可。考场做法拿了 40pts。

正解

上面的做法,我们发现可以拓展到全局,也就是同时有 \(k\) 个序列的情况。

我们考虑一个括号序列的常用 trick:把左括号看作 \(+1\),把右括号看作 \(-1\),一个括号序列合法,当且仅当其总和为 \(0\) 且任何一段前缀和都 \(\ge 0\)。

总和为 \(0\) 很好考虑,我们主要想任何一段前缀和 \(\ge 0\) 怎么搞。

观察到前缀和数组每次相对前一项的变化量要么是 \(1\) 要么是 \(-1\),并且由于先要保证能找到,所以我们先找出可以匹配的左括号的区间左端点。

但是这样并不好做,因为如果 \([l,r]\) 的和 \(<0\),\([l-1,r]\) 的和却不一定 \(<0\)。所以固定右括号的方式不可行。

因此,我们才考虑固定左括号,去寻找右括号,并且把 dp 倒着做。

于是找出前缀和 \(<0\) 的就很简单了,对于一个左括号,其最多能匹配到的右括号一定在后面离自己最近的使前缀和 \(<0\) 的地方。

这个我们可以通过从后往前扫描,记录下考虑序列第 \(i\) 位到第 \(n\) 位里面前缀和为每一种数的最小下标,这样我们就可以快速查询在左括号后面,第一个使前缀和 \(<0\) 的右括号在哪了。

如果找不到,就说明右括号在右边的哪里都可以,所以赋为最大值。

算完最大右端点后,我们对于每一列,求出其最大右端点中的最小值,这就是某一列里可能的匹配范围。

接下来考虑总和为 \(0\) 的限制,很容易发现对于前缀和数组而言是这样的:

\[f[i]-f[j-1]=0
\]

可得:

\[f[i]=f[j-1]
\]

一个括号序列合法,必须每一行都满足这个条件,也就是说对于两个列而言,每一行的前缀和相同,它才可能合法。

所以我们对每一列哈希,存进 unordered_map,然后统计离自己最近的且在最大右端点左边的相同位即可。

最后来个 dp 就完事了,时间是 \(O(nk)\) 的,但 unordered_map 可能有点常数。

代码

代码还是比较好写的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const ll eps=500005,mod=998244353;
int n,k,a[15][50005],f[15][50005],tot[15][110005],r[15][50005],pr[50005],y[50005];
ll hs[50005],dp[50005],ans;
unordered_map<ll,int>mp;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>k>>n;
//处理原括号序列、前缀和数组、各列的哈希值
for(int i=1;i<=k;i++)
{
for(int j=1;j<=n;j++)
{
char c;
cin>>c;
if(c=='(')a[i][j]=1;
else a[i][j]=-1;
f[i][j]=f[i][j-1]+a[i][j];
hs[j]=(hs[j]*10007%mod+f[i][j])%mod;
}
}
//统计右边最远可达的括号
memset(tot,0x3f,sizeof(tot));
memset(r,0x3f,sizeof(r));
for(int i=1;i<=k;i++)
{
for(int j=n;j>=1;j--)
{
tot[i][f[i][j]+eps]=j;
r[i][j]=tot[i][f[i][j-1]-1+eps];
}
}
//记录对于每一列而言的右边界
memset(pr,0x3f,sizeof(pr));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
pr[i]=min(pr[i],r[j][i]);
}
}
//找出相同的哈希值
for(int i=n;i>=1;i--)
{
mp[hs[i]]=i;
y[i]=mp[hs[i-1]];
}
//dp
for(int i=n;i>=1;i--)
{
if(y[i]!=0&&y[i]<=pr[i])
{
dp[i]=dp[y[i]+1]+1;
}
}
//统计答案
for(int i=1;i<=n;i++)
{
ans+=dp[i];
}
cout<<ans;
return 0;
}

Luogu P3059 Concurrently Balanced Strings G 题解 [ 紫 ] [ 线性 dp ] [ 哈希 ] [ 括号序列 ]的更多相关文章

  1. [USACO12NOV]同时平衡线Concurrently Balanced Strings DP map 思维

    题面 [USACO12NOV]同时平衡线Concurrently Balanced Strings 题解 考虑DP. \(f[i]\)表示以\(i\)为左端点的合法区间个数.令\(pos[i]\)表示 ...

  2. [Usaco2012 Nov]Concurrently Balanced Strings

    Description [Brian Dean, 2012] Farmer John's cows are all of a very peculiar breed known for its dis ...

  3. 【区间DP】codevs3657 括号序列题解

    题目描述 Description 我们用以下规则定义一个合法的括号序列: (1)空序列是合法的 (2)假如S是一个合法的序列,则 (S) 和[S]都是合法的 (3)假如A 和 B 都是合法的,那么AB ...

  4. 【基础练习】【线性DP】codevs2622 数字序列(最大连续子序列和)题解

    版权信息 转载请注明出处 [ametake版权全部]http://blog.csdn.net/ametake欢迎来看 这道题目本质就是朴素的最大连续子序列和 直接上题目和代码 题目描写叙述 Descr ...

  5. 【AHOI2009】中国象棋 题解(线性DP+数学)

    前言:这题主要是要会设状态,状态找对了问题迎刃而解. --------------------------- 题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可 ...

  6. 洛谷 P1360 [USACO07MAR]Gold Balanced Lineup G (前缀和+思维)

    P1360 [USACO07MAR]Gold Balanced Lineup G (前缀和+思维) 前言 题目链接 本题作为一道Stl练习题来说,还是非常不错的,解决的思维比较巧妙 算是一道不错的题 ...

  7. 线段树||BZOJ5194: [Usaco2018 Feb]Snow Boots||Luogu P4269 [USACO18FEB]Snow Boots G

    题面:P4269 [USACO18FEB]Snow Boots G 题解: 把所有砖和靴子排序,然后依次处理每一双靴子,把深度小于等于它的砖块都扔线段树里,问题就转化成了求线段树已有的砖块中最大的砖块 ...

  8. [LeetCode]1221. Split a String in Balanced Strings

    Balanced strings are those who have equal quantity of 'L' and 'R' characters. Given a balanced strin ...

  9. 【leetcode】1221. Split a String in Balanced Strings

    题目如下: Balanced strings are those who have equal quantity of 'L' and 'R' characters. Given a balanced ...

  10. 洛谷P3104 Counting Friends G 题解

    题目 [USACO14MAR]Counting Friends G 题解 这道题我们可以将 \((n+1)\) 个边依次去掉,然后分别判断去掉后是否能满足.注意到一点, \(n\) 个奶牛的朋友之和必 ...

随机推荐

  1. (系列十三)Vue3+Echarts搭建超好看的系统面板

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  2. ZCMU-1038

    其实感觉不太难,读懂题意就行,我一开始没有仔细去读感觉就很懵.其题目意思就是一段字符串含有数字和'<'或者'>',一开始从左开始遍历,遇到'>'这类东西换方向,如果有多次遇到就删之前 ...

  3. 鸿蒙UI开发快速入门 —— part12: 渲染控制

    1.前言 在声明式描述语句中开发者除了使用系统组件外,还可以使用渲染控制语句来辅助UI的构建,这些渲染控制语句包括控制组件是否显示的条件渲染语句,基于数组数据快速生成组件的循环渲染语句. 2.条件渲染 ...

  4. 不错的AI项目集合

    最近收集了一些不错的AI项目. AutoGPT:AutoGPT 的愿景是让每个人都可以使用和构建无障碍 AI.我们的使命是提供工具,以便您可以专注于重要的事情. Stable Diffusion we ...

  5. Nginx基础使用

    Nginx Nginx是一个WEB服务 [1].安装nginx 1.官网安装 2.使用默认的仓库安装,版本较低 3.编译方式安装,需要其他功能模块的时候 自定义安装 # 基于官网仓库的安装方式,版本较 ...

  6. zz 失血模型与充血模型等

    失血模型与充血模型 | 三秋 (贫血模型)优点是系统的层次结构清楚,各层之间单向依赖,Client->(BusinessFacade)->BusinessLogic->Data Ac ...

  7. 【Python基础练习】实验3:列表、字典、集合

    实验3:列表.字典.集合 姓名:萌狼蓝天 时间:2023年11月6日 Python:3.12 博客:https://wwww.mllt.cc 实验目的 (1)了解列表.元组.字典和集合的概念 (2)学 ...

  8. 【前端】【H5 API】地理定位(获取经纬度)

    H5 API 地理定位 地理定位在日常生活中应用比较广泛,例如,互联网打车.在线地图等.在HTML 5的规范中,增加了获取用户地理位置信息的接口Geolocation,开发者可以通过经纬度来获取用户的 ...

  9. 【MyBatis】学习笔记12:通过级联属性赋值解决多对一的映射关系

    [Mybatis]学习笔记01:连接数据库,实现增删改 [Mybatis]学习笔记02:实现简单的查 [MyBatis]学习笔记03:配置文件进一步解读(非常重要) [MyBatis]学习笔记04:配 ...

  10. function keyword is non-standard. Delete it.

    SC2112 – ShellCheck Wiki See this page on GitHub function keyword is non-standard. Delete it. Proble ...