cf1598f RBS
定义括号序列为只包括 \(\texttt{(}\) 和 \(\texttt{)}\) 的字符串。一个匹配的括号序列 (简记为 RBS) 满足,可以在其中加入 \(1\) 和 \(+\),将其转化为合法的代数式,例如:
- \(\texttt{()()}\) 和 \(\texttt{(())}\) 是匹配的
- \(\texttt{)(}\) 和 \(\texttt{(}\) 和 \(\texttt{)}\) 不是
将两个字符串拼接在一起简记为 \(x+y\) 。例如,\(\texttt{()()}+\texttt{)(}=\texttt{()())(}\) .
给定 \(n\) 个括号序列 \(s_1\sim s_n\) ,你可以将他们任意重新排序,要求使得最终排序后的字符串满足 \(s_1+\dots+s_n\) 的 RBS 前缀个数最多。
\(1\leq n\leq 20,\sum |s_i|\leq 4\cdot 10^5\)
\(20=\) 状压 .
\(dp(S)\) 表示已经选择集合为 \(S\) 的字符串的最大代价.
首先,如果 \(S\) 中的字符串排列出现 \(<0\) 的位置肯定不合法. 但是,观察得到,对于状态 \(S\) ,\(\texttt{(}\) 的个数 \(-\) \(\texttt{)}\) 的个数的值是一定的,所以,对于新加入字符串能产生贡献的位置也是固定的. 考虑加入 \(s_i\) ,设加入 \(s_i\) 的贡献是 \(w\) ,那么出现两种情况,加入 \(s_i\) 之后使得整个字符串出现 \(<0\) 的位置,那么,\(dp(S)+w\) 与答案取最大值即可;否则,用 \(dp(S)+w\) 去跟新 \(dp(s+i)\) .
现在剩下的问题就是如果求出对于 \(s_i\) 中每种位置的贡献,产生贡献的位置必定是前缀最小值,并且一定是非正数,所以考虑先处理出这些位置,发现单调不增,那么相同的一段的数量就是当前此种位置的贡献了.
(本来是看到 ds 的 tag 才去做的,ds 呢,用到 ds 了吗?)
时间复杂度 : \(O(\sum|s_i|+2^n)\)
空间复杂度 : \(O(2^n+\sum |s_i|)\)
code
#include
using namespace std;
const int N=20,L=4e5+10;
int n;
string str[N];
int w[N][L],mn[N];
int a[N],sum[1<v;
int tmp=0;
for(int i=0;i<(int)s.size();i++){
tmp+=s[i]==')'?-1:1;
mn[id]=min(mn[id],tmp);
if(s[i]==')'&&tmp<=0&&tmp==mn[id])v.push_back(-tmp);
}
a[id]=tmp;
for(int i=0;i<(int)v.size();i++){
int cnt=0,j=i;
for(j=i;j<(int)v.size();j++){
if(v[i]==v[j])++cnt;
else break;
}
w[id][v[i]]=cnt;
i=j-1;
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=0;i>str[i];
memset(mn,0x3f3f3f3f,sizeof(mn));
for(int i=0;i