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

phigos
**搬运于
2025-08-24 22:46:50,当前版本为作者最后更新于2023-08-29 10:44:52,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意简化
给出 个 ,仅由 和 构成的字符串,将 看做左括号, 看做右括号。
求任意两个串 和 收尾相接后,本质不同的合法括号序列的个数。
思路
设左括号价值为 ,右括号价值为 ,那么一个序列合法的充要条件是:
-
任意前缀的价值和非负
-
总价值为
-
任意后缀的价值和非正
考虑将 与 拼接后的序列的一个子序列分成前缀和后缀,使得前缀在 中,后缀在 中,那么就可以分别在 和 中求出价值为 和 的子序列的方案数。
dp
对于 串,设 表示 串中,考虑前 位,价值和为 ,且以左括号 右括号 空串结尾的方案数。
对于 串,设 表示 中,考虑后 位,价值和为 ,且以左括号 右括号 空串开头的方案数。
初始化
显然,初始为空串,只有一种情况,即:
考虑如何转移
若当前位置 为 :
若当前位置 为 :
去重
但如果直接将 和 乘起来统计答案会重复。

如图,选择 中 左侧加上 以 开头的情况,会和选择 中以 结尾加上 中 右侧的情况重复。
因此还需要一个数组 表示 串中,考虑前 位,价值和为 , 右侧是否有左括号,是否有右括号的方案数。
设 为 右侧剩余的左or右括号数,就有:
位置为左括号:
$$g_{a,j,k,l>0,r>0}\gets g_{a,j-1,k,l>0,r>0}+front_{a,j-1,k-1,0/1/2}-front_{a,j-1,k,0} $$位置为右括号:
$$g_{a,j,k,l>0,r>0}\gets g_{a,j-1,k,l>0,r>0}+front_{a,j-1,k+1,0/1/2}-front_{a,j-1,k,1} $$像这样,只要 串后面没有 串开头的括号,就不会出现上面的重复情况。
统计答案
设答案为 。
若 串以左括号开头,那么 的末尾不能有左括号; 若 串以右括号开头,那么 的末尾不能有右括号; 若 串为空串,那么 没有限制。
$$ans\gets (g_{a,n,k,0,0}+g_{a,n,k,0,1})\times back_{b,1,k,0} $$$$ans\gets (g_{a,n,k,0,0}+g_{a,n,k,1,0})\times back_{b,1,k,1} $$$$ans\gets (g_{a,n,k,0,0}+g_{a,n,k,0,1}+g_{a,n,k,1,0}+g_{a,n,k,1,1})\times back_{b,1,k,2} $$空间优化
不难发现,三个 数组都要开到 ,喜提MLE。
但是,对于 这一维,我们每次转移都只用到了前一位的结果,并且统计答案只用到 ,因此可以通过调整循环的方向压掉 这一维。
时间复杂度为 ,可以通过本题。
记得取模!!!
代码
#include<bits/stdc++.h> #define int long long #define inf (1ll<<60) using namespace std; const int N=605,mod=1e9+7; int n,front[N][N][3],back[N][N][3],g[N][N][2][2]; char s[N]; signed main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>(s+1); int num=strlen(s+1); int l=0,r=0; for(int j=1;j<=num;j++) s[j]=='L'?l++:r++;//j右侧左/右括号的个数 front[i][0][2]=1; back[i][0][2]=1; g[i][0][(bool)l][(bool)r]=1; for(int j=1;j<=num;j++){//正序处理前缀 if(s[j]=='L'){l--; for(int k=600;k>=1;k--){ (g[i][k][(bool)l][(bool)r]+=front[i][k-1][0]+front[i][k-1][1]+front[i][k-1][2]-front[i][k][0]+mod)%=mod; (front[i][k][0]=front[i][k-1][0]+front[i][k-1][1]+front[i][k-1][2])%=mod; } }else{r--; for(int k=0;k<600;k++){ (g[i][k][(bool)l][(bool)r]+=front[i][k+1][0]+front[i][k+1][1]+front[i][k+1][2]-front[i][k][1]+mod)%=mod; (front[i][k][1]=front[i][k+1][0]+front[i][k+1][1]+front[i][k+1][2])%=mod; } } } for(int j=num;j>=1;j--){//倒序处理后缀 if(s[j]=='L') for(int k=0;k<600;k++) (back[i][k][0]=back[i][k+1][0]+back[i][k+1][1]+back[i][k+1][2])%=mod; else for(int k=600;k>=1;k--) (back[i][k][1]=back[i][k-1][0]+back[i][k-1][1]+back[i][k-1][2])%=mod; } } for(int i=1;i<=n;i++){//枚举a串 for(int j=1;j<=n;j++){//枚举b串 int ans=mod-1; for(int k=0;k<=600;k++){ (ans+=(g[i][k][0][0]+g[i][k][0][1])*back[j][k][0]%mod)%=mod; (ans+=(g[i][k][0][0]+g[i][k][1][0])*back[j][k][1]%mod)%=mod; (ans+=(g[i][k][0][0]+g[i][k][0][1]+g[i][k][1][0]+g[i][k][1][1])*back[j][k][2]%mod)%=mod; } cout<<ans<<' '; } putchar('\n'); } return 0; } -
- 1
信息
- ID
- 8685
- 时间
- 9000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者