【五校联考1day1】已经没有什么好害怕的了 题解
看一个例子()()()()
,在结尾加上(
。
上面这个图就是表示了红色的数字就是下一个左括号的红色数字加一(右括号的数字为111),新加的左括号为111;蓝色数字就是上一个左括号的蓝色数字减一(右括号的数字为−1-1−1),第一个左括号为−1-1−1。然后其实就是递推求出这些数字。而红蓝色数组的和就是一个差分数组,表示这一个数与上一个数的差值,所以就前缀和一下边算边计算答案。
怎么证明上述方法是正确的呢?下面设红色数组为aaa,蓝色为bbb。
则a1+b1=ans1a_1+b_1=ans_1a1+b1=ans1,这是显然的,也是正确的。然后对于每一个右括号位置iii,有ai+bi=0a_i+b_i=0ai+bi=0,这是因为右括号其实就是与其配对的左括号的值。
考虑剩下的左括号(只对于上面的例子):
下面iii为第几个左括号。
对于i=1i=1i=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=2i=2i=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=3i=3i=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=4i=4i=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]。
其实有个显然的事情,对于iii,有一些区间一定有一些来自i−1i-1i−1的区间,而有一些则被删掉了,∣bi∣−1|b_i|-1∣bi∣−1为删掉的数量,所以之所以有bi=bj−1(j<i)b_i=b_j-1(j<i)bi=bj−1(j<i)(jjj是上一个左括号的位置),原因就是删掉的数量是每一次比上次多了111。对于aaa同理。
大家可以手磨一些数据,可以发现更好的规律。
#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);
}
}