【JZOJ4209】已经没有什么好害怕的了【差分】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/4209
给出一个括号序列,设ansians_i表示经过ii的合法的串,合法的串指所有括号都可以匹配且没有穿插。


思路:

next[i]next[i]表示与(前括号)ii相匹配的后括号位置,last[i]last[i]表示与(后括号)ii相匹配的前括号的位置。这个可以用栈O(n)O(n)求出。
假设这个序列是这样的
())((())())(())())((())())(())
先只看最外层的括号
())(......)(......)())(......)(......)
将无用的括号分离
()  (......)(......)()\ |\ (......)(......)
会发现,每个括号所影响到的组数是递增的。
而每个后括号影响是递减的。
于是就用差分维护。
然后对于括号内的,再递归求解即可。
每个括号最多访问两次,时间复杂度O(n)O(n)


代码:

#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

const int MOD=1000000007;
const int N=1000010;
int T,len,next[N],last[N];
char ch[N];
ll sum[N],ans;

void work1(int l,int r)  //前括号差分
{
	if (l>r) return;
	ll k=0;
	for (int i=r;i>=l;i--)
		if (last[i])
		{
			work1(last[i]+1,i-1);  //递归
			k++;
			sum[last[i]]+=k;
			i=last[i];
		}
		else k=0;
}

void work2(int l,int r)  //后括号
{
	if (l>r) return;
	ll k=0;
	for (int i=l;i<=r;i++)
		if (next[i])
		{
			work2(i+1,next[i]-1);
			k++;
			sum[next[i]+1]-=k;
			i=next[i];
		}
		else k=0;
}

int main()
{
	scanf("%d",&T);
	while (T--)
	{
		memset(sum,0,sizeof(sum));
		memset(next,0,sizeof(next));
		memset(last,0,sizeof(last));
		memset(sum,0,sizeof(sum));
		scanf("%s",ch+1);
		len=strlen(ch+1);
		stack<int> s;
		for (int i=1;i<=len;i++)
			if (ch[i]=='(') s.push(i);
				else if (s.size())
				{
					next[s.top()]=i;
					last[i]=s.top();  //利用栈求
					s.pop();
				}
		work1(1,len);
		work2(1,len);
		ans=0;
		for (int i=1;i<=len;i++)
		{
			sum[i]+=sum[i-1];
			ans+=sum[i]*(ll)i%MOD;
		}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2019-01-21 21:45  全OI最菜  阅读(172)  评论(0编辑  收藏  举报