【五校联考1day1】已经没有什么好害怕的了 题解
看一个例子()()()()
,在结尾加上(
。
上面这个图就是表示了红色的数字就是下一个左括号的红色数字加一(右括号的数字为 1 1 1),新加的左括号为 1 1 1;蓝色数字就是上一个左括号的蓝色数字减一(右括号的数字为 − 1 -1 −1),第一个左括号为 − 1 -1 −1。然后其实就是递推求出这些数字。而红蓝色数组的和就是一个差分数组,表示这一个数与上一个数的差值,所以就前缀和一下边算边计算答案。
怎么证明上述方法是正确的呢?下面设红色数组为 a a a,蓝色为 b b b。
则 a 1 + b 1 = a n s 1 a_1+b_1=ans_1 a1+b1=ans1,这是显然的,也是正确的。然后对于每一个右括号位置 i i i,有 a i + b i = 0 a_i+b_i=0 ai+bi=0,这是因为右括号其实就是与其配对的左括号的值。
考虑剩下的左括号(只对于上面的例子):
下面 i i i为第几个左括号。
对于 i = 1 i=1 i=1,其区间有 [ 1 , 2 ] , [ 1 , 4 ] , [ 1 , 6 ] , [ 1 , 8 ] [1,2],[1,4],[1,6],[1,8] [1,2],[1,4],[1,6],[1,8]。
对于 i = 2 i=2 i=2,其区间有 [ 1 , 4 ] , [ 1 , 6 ] , [ 1 , 8 ] , [ 3 , 4 ] , [ 3 , 6 ] , [ 3 , 8 ] [1,4],[1,6],[1,8],[3,4],[3,6],[3,8] [1,4],[1,6],[1,8],[3,4],[3,6],[3,8]。
对于 i = 3 i=3 i=3,其区间有 [ 1 , 6 ] , [ 1 , 8 ] , [ 3 , 6 ] , [ 3 , 8 ] , [ 5 , 6 ] , [ 5 , 8 ] [1,6],[1,8],[3,6],[3,8],[5,6],[5,8] [1,6],[1,8],[3,6],[3,8],[5,6],[5,8]。
对于 i = 4 i=4 i=4,其区间有 [ 1 , 8 ] , [ 3 , 8 ] , [ 5 , 8 ] , [ 7 , 8 ] [1,8],[3,8],[5,8],[7,8] [1,8],[3,8],[5,8],[7,8]。
其实有个显然的事情,对于 i i i,有一些区间一定有一些来自 i − 1 i-1 i−1的区间,而有一些则被删掉了, ∣ b i ∣ − 1 |b_i|-1 ∣bi∣−1为删掉的数量,所以之所以有 b i = b j − 1 ( j < i ) b_i=b_j-1(j<i) bi=bj−1(j<i)( j j j是上一个左括号的位置),原因就是删掉的数量是每一次比上次多了 1 1 1。对于 a a a同理。
大家可以手磨一些数据,可以发现更好的规律。
#include<bits/stdc++.h>
using namespace std;
int t,n,b[1000005],c[1000005],d[1000005],e[1000005],s[1000005];
char a[1000005];
typedef long long ll;
const int mod=1e9+7;
int main(){
scanf("%d",&t);
while (t--){
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
memset(e,0,sizeof(e));
scanf("%s",a+1);
n=strlen(a+1);
int top=0;
for (int i=1;i<=n;i++){
if (a[i]=='(') s[++top]=i;
else{
if (top>0){
b[i+1]=s[top];
c[s[top]]=i+1;
top--;
}
}
}
for (int i=n+1;i>=1;i--) d[i]=1;
for (int i=n+1;i>=1;i--) d[b[i]]+=d[i];
for (int i=1;i<=n;i++) e[i]=-1;
for (int i=1;i<=n;i++) e[c[i]]+=e[i];
ll sum=0;
ll ans=0;
for (int i=1;i<=n;i++){
sum+=(ll)e[i]+(ll)d[i];
ans+=(ll)((1ll*sum*i)%mod);
}
printf("%lld\n",ans);
}
}