2022/8/26 总结
A.括号序列
-
一看,我趣,字符串,直接抱灵吧,让我死得体面一点……
-
再一看数据范围,我趣,\(\mathtt{O(n)}\),这怎么写得出来啊?
Solution
-
虽然是 \(\mathtt{O(7n)}\),但卡卡常也就卡过了;
-
先遍历一遍,把括号两两匹配一下,记录一下另一半括号的位置。再来两边 \(\mathtt{O(n)}\),处理出可以形如 \(\mathtt{()()}\) 这样合并的括号个数的前缀和(\(sum_0\))与后缀和(\(sum_1\));
-
然后一遍 \(\mathtt{O(n)}\),统计上面这种方式合并每个位置的答案(为 \((sum_{0,i}-1)\times(sum_{1,pos_i}+1)\))。
-
考虑如何处理括号套括号的情况。当一对括号里面套着很多其他的合法字符串时,将这个括号及其里面视作一个整体的答案贡献上一步已经算过了。那么对于最外层的括号,里面括号怎么匹配不会对它产生影响,因为如果要这层括号对答案有贡献,里面的括号一定全部在子串里。所以只需要对里面的括号修改即可。
里面的括号对于同级的匹配已经算过了,只需要加上当它作为大括号的整个串时的贡献即可。但这一步是区间修改,所以先跑一遍把答案数组处理成差分。 -
最后统计答案的时候记得取余就行了……
要不是这离谱的取余规则我至于吗,万恶之源啊
AC code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
const int mod=1e9+7;
const int N=1e7+10;
char ch[N];
int n;
ll ans[N];
int pos[N];
ll sum[2][N];
int st[N];
int main(){
// freopen("bracket.in","r",stdin);
// freopen("bracket.out","w",stdout);
scanf("%s",ch+1);
n=strlen(ch+1);
re int r=0;
for(re int i=1;i<=n;++i){
if(ch[i]=='(')
st[++r]=i;
else if(ch[i]==')'){
if(r){
pos[i]=st[r];
pos[st[r]]=i;
--r;
}
}
}
for(re int i=1;i<=n;++i)
if(pos[i]<i && pos[i])
sum[0][i]=sum[0][pos[i]-1]+1;
for(re int i=n;i;--i)
if(pos[i]>i)
sum[1][i]=sum[1][pos[i]+1]+1;
for(re int i=1;i<=n;++i){
if(!pos[i]) continue;
if(i<pos[i])
ans[i]+=1ll*(sum[0][i-1]+1)*(sum[1][pos[i]+1]+1);
else ans[i]+=1ll*(sum[0][pos[i]-1]+1)*(sum[1][i+1]+1);
}
for(re int i=n;i;--i)
ans[i]-=ans[i-1];
re ll cnt;
for(re int i=1;i<=n;++i){
if(!pos[i]) continue;
if(i>pos[i]) continue;
if(pos[i]==i+1) continue;
cnt=1ll*(sum[0][i-1]+1)*(sum[1][pos[i]+1]+1);
ans[i+1]+=cnt;
ans[pos[i]]-=cnt;
}
cnt=0;
re ll ans0=0;
for(re int i=1;i<=n;++i){
(cnt+=0ll+ans[i])%=mod;
// (cnt+=0ll+mod)%=mod;
ans0+=(1ll*cnt*i)%mod;
}
printf("%lld\n",ans0);
return 0;
}
/*
需要 O(n)以内的算法。
希望取余不要挂!!
*/