Newcoder 111 A.托米的简单表示法(树形DP)

Description

一天,他正在为解析算术表达式的课程准备课件。 在课程的第一部分,他只想专注于解析括号。 他为他的学生发明了一个有趣的正确括号序列的几何表示,如下图所示:

在这里插入图片描述

几何表示的定义:

1.对于一个括号序列 A A A,我们定义 g ( A ) g(A) g(A) A A A的几何表示形式,则 “ ( ) " “()" ()"的表示是一个 1 ∗ 1 1*1 11的方块,高度为 1 1 1;

2.对于一个括号序列 A A A “ ( A ) " “(A)" (A)"的表示是由一个比 g ( A ) g(A) g(A) 2 2 2个单位高 1 1 1个单位的矩形包围 g ( A ) g(A) g(A),它的高度为 A + 1 A+1 A+1;

3.对于两个括号序列 A A A B B B A + B A+B A+B的几何表示形式为把 g ( B ) g(B) g(B)放置在 g ( A ) g(A) g(A)右边的一个单位,且高度为 A A A B B B的高度的较大值。其中+指的是字符串的连接符。​

在完成课件后,托米老师开始玩他做好的图片。 他将图像的有限区域交替地涂成黑色和白色,使最外面的区域全部涂成黑色。 对于上面的例子,这个着色如下所示:

img

现在给你一个合法的括号序列。 请计算颜色为黑色的区域的面积。

Input

输入的第一行包含一个整数 T T T,表示指定测试用例的数量。

每个测试用例前面都有一个空白行。

每个测试用例由一个合法括号序列 s s s组成。 每行只包含字符 ′ ( ′ '(' ( ′ ) ′ ')' )

( 1 ≤ T ≤ 10 , ∣ s ∣ ≤ 4 ⋅ 1 0 5 ) (1\le T\le 10,|s|\le 4\cdot 10^5) (1T10,s4105)

Output

对于每个测试用例,输出一行包含一个整数,表示相应几何表示的黑色部分的面积。

Sample Input

2

((()))

(())(()(()))

Sample Output

10
20

Solution

将该合法括号序列看作一个森林的 d f s dfs dfs序,左括号表示从当前节点走向一个新的儿子节点,右括号则表示从当前节点走向父亲节点,建树后考虑 u u u节点所代表左括号和与之配对的右括号形成的区域中黑色块的面积,记为 d p ( u ) dp(u) dp(u),为了维护面积,多维护两个值 h ( u ) , w ( u ) h(u),w(u) h(u),w(u)分别表示只考虑以 u u u为根的子树中所有节点构成区域的高度和宽度,那么显然有转移
h ( u ) = max ⁡ v ∈ s o n ( u ) { h ( v ) } + 1 , w ( u ) = ∣ s o n ( u ) ∣ + 1 + ∑ v ∈ s o n ( u ) w ( v ) h(u)=\max\limits_{v\in son(u)}\{h(v)\}+1,w(u)=|son(u)|+1+\sum\limits_{v\in son(u)}w(v) h(u)=vson(u)max{h(v)}+1,w(u)=son(u)+1+vson(u)w(v)
进而有
d p ( u ) = h ( u ) ⋅ w ( u ) − ∑ v ∈ s o n ( u ) d p ( v ) dp(u)=h(u)\cdot w(u)-\sum\limits_{v\in son(u)}dp(v) dp(u)=h(u)w(u)vson(u)dp(v)
累加该森林每棵树根节点的 d p dp dp值即为答案,时间复杂度 O ( n ) O(n) O(n)

Code

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=400005;
int T,fa[maxn],h[maxn],w[maxn];
char c[maxn]; 
vector<int>g[maxn];
ll dp[maxn];
void dfs(int u)
{
	if(g[u].size()==0)
	{
		h[u]=w[u]=dp[u]=1;
		return ;
	}
	h[u]=dp[u]=0,w[u]=g[u].size()+1;
	for(int i=0;i<g[u].size();i++)
	{
		int v=g[u][i];
		dfs(v);
		h[u]=max(h[u],h[v]+1);
		w[u]+=w[v];
		dp[u]-=dp[v];
	}
	dp[u]+=(ll)h[u]*w[u];
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",c);
		int n=strlen(c);
		for(int i=0;i<n;i++)g[i].clear(),fa[i]=-1;
		int u=0;
		for(int i=1;i<n;i++)
			if(c[i]=='(')g[u].push_back(i),fa[i]=u,u=i;
			else u=fa[u];
		ll ans=0;
		for(int i=0;i<n;i++)
			if(c[i]=='('&&fa[i]==-1)
			{
				dfs(i);
				ans+=dp[i];
			}
		printf("%lld\n",ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值