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)以内的算法。 

希望取余不要挂!! 

*/
posted @ 2022-08-26 19:08  Star_LIcsAy  阅读(34)  评论(0)    收藏  举报