【五校联考1day1】已经没有什么好害怕的了

此题需: 分治,差分
首先要知道,除了(),()()这样的是合法的,(()(()))()这样复杂的串,也是合法的。
要申清题意。
每个左括号都至多有一个匹配的右括号,所以我们可以
先用一个栈预处理出tail[] 和 bz[]数组。
bz[i]表示如果i是左括号,有无与之匹配的右括号。
tail[i]表示在bz[i]==1 的基础上,i这个左括号所对应的右括号位置编号
先把s从左到右扫一遍先算一遍“()”这种情况每个位置的贡献
考虑对s的每一层括号进行剖析
显然找到s的第一个左括号后,i再不停跳往tail[i]+1,这样就能把第一层(左)括号全都跳完。
我们用sol(l,r)函数来处理l~r区间的答案
所以说每次跳到一个左括号i,就sol(i,tail[i])
然后进入这段区间,
我们进入了第二层括号主宰的世界,在这个世界里,没有第一层括号的包围,也没有更深层括号的干扰,于是这段区间就成了这样:
()()()…
1 2 3 …
这样来统计包含每对括号的字串个数,显而易见,设i这对括号前面有lp对括号,后面有rp对,那么包含i的子串个数就有lp+rp+lprp个
然后,找第二层括号。

我们进入了第3层括号主宰的世界,在这个世界里,没有第1、2层括号的包围,也没有更深层括号的干扰,于是这段区间就成了这样:
()()()
1 2 3。。。
…不断递归。
回到第一层括号主宰的世界。是这样的:
()()()%%%234234246()()¥@#¥234()
中间是没用的括号。
虽然如此,还是会有几对括号手牵手
我们这样改造s:
(()()())%%%234234246(()())¥@#¥234(())
到了第0层括号主宰的世界。
同理,sol这些0层括号中间的东西,区别是不用dg了。
不要问我为什么不用好吗
最后捋一遍差分数组,出答案,AC

#include<bits/stdc++.h>
#define N 1000010
#define ll long long
#define mod (ll)(1e9+7)
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;

struct interval
{
	ll l,r;
}a[N];

ll T,i,j,k,t,n,top,tot;
ll c[N],st[N][2],tail[N],bz[N],ans[N],flag[N];
char s[N];
ll lp=0,rp=0;

void init()
{
	for(i=1;i<=n;i++)tail[i]=i;
	top=0;
	for(i=1;i<=n;i++)
	{
		st[++top][0]=i,st[top][1]=(s[i]=='(') ? 1:2;
		if(top>1 && st[top][1]==2 && st[top-1][1]==1 )
		{
			tail[st[top-1][0]]=i;
			bz[st[top-1][0]]=1;
			top-=2;
		}
	}
	return ;
}
void solve(ll l,ll r)//解决(l,r)内部 
{
	if(r<=l)return ;
	tot=0;
	for(ll i=l;i<=r;i=tail[i]+1)if(bz[i])++tot;
	lp=0,rp=tot-1;
	for(ll i=l;i<=r;i=tail[i]+1)if(bz[i])
	{
		c[i]+=lp+rp+lp*rp;
		c[tail[i]+1]-=lp+rp+lp*rp;
		++lp,--rp;
	}
	for(ll i=l;i<=r;i=tail[i]+1)if(bz[i])solve(i+1,tail[i]-1);
	return ;
}
int main()
{
	scanf("%lld\n",&T);
	while(T--)
	{
		memset(c,0,sizeof c);
		memset(ans,0,sizeof ans);
		memset(bz,0,sizeof bz);
		memset(a,0,sizeof a);
		for(i=0;i<N-10;i++)s[i]='\0';
		scanf("%s\n",s+1);
		n=strlen(s+1);
		init(); // tail,bz
		ll m=0;
		for(i=1;i<=n;i++) if(bz[i])c[i]+=1,c[tail[i]+1]-=1;
		for(i=1;i<=n;i=tail[i]+1)if(bz[i])
			a[++m]=(interval){i,tail[i]},solve(i+1,tail[i]-1);
		ll next=-1;
		for(i=1;i<=m;i=next)
		{
			ll l=i,r=i,lp=0,rp=0;
			while(r<m && a[r+1].l == a[r].r+1) ++r;
			ll t=r-l+1;
			next=r+1;
			r=i , rp=t-1;;
			c[a[l].l]+=lp+rp+lp*rp; c[a[l] .r +1]-=lp+rp+lp*rp;
			while(r<m && a[r+1].l == a[r].r+1) 
			{
				++r;
				++lp;
				--rp;
				c[a[r].l]+=lp+rp+lp*rp ; c[a[r] . r+1]-=lp+rp+lp*rp;
			}		
		}
		
		ll sum=0;
		for(i=1;i<=n;i++)sum+=c[i],ans[i]=sum;
		for(sum=0,i=1;i<=n;i++)sum+=(ans[i]*i)%mod;
		printf("%lld\n",sum);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值