1. 程式人生 > >Newcoder 111 A.托米的簡單表示法(樹形)

Newcoder 111 A.托米的簡單表示法(樹形)

Description

一天,他正在為解析算術表示式的課程準備課件。 在課程的第一部分,他只想專注於解析括號。 他為他的學生髮明瞭一個有趣的正確括號序列的幾何表示,如下圖所示:

在這裡插入圖片描述

幾何表示的定義:

1.對於一個括號序列 A A ,我們定義 g (

A ) g(A) A A 的幾何表示形式,則 ( )
" “()"
的表示是一個 1 1 1*1 的方塊,高度為 1
1
;

2.對於一個括號序列 A A ( A ) " “(A)" 的表示是由一個比 g ( A ) g(A) 2 2 個單位高 1 1 個單位的矩形包圍 g ( A ) g(A) ,它的高度為 A + 1 A+1 ;

3.對於兩個括號序列 A A B B A + B A+B 的幾何表示形式為把 g ( B ) g(B) 放置在 g ( A ) g(A) 右邊的一個單位,且高度為 A A B B 的高度的較大值。其中+指的是字串的連線符。​

在完成課件後,托米老師開始玩他做好的圖片。 他將影象的有限區域交替地塗成黑色和白色,使最外面的區域全部塗成黑色。 對於上面的例子,這個著色如下所示:

img

現在給你一個合法的括號序列。 請計算顏色為黑色的區域的面積。

Input

輸入的第一行包含一個整數 T T ,表示指定測試用例的數量。

每個測試用例前面都有一個空白行。

每個測試用例由一個合法括號序列 s s 組成。 每行只包含字元 ( '(' ) ')'

( 1 T 10 , s 4 1 0 5 ) (1\le T\le 10,|s|\le 4\cdot 10^5)

Output

對於每個測試用例,輸出一行包含一個整數,表示相應幾何表示的黑色部分的面積。

Sample Input

2

((()))

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

Sample Output

10
20

Solution

將該合法括號序列看作一個森林的 d f s dfs 序,左括號表示從當前節點走向一個新的兒子節點,右括號則表示從當前節點走向父親節點,建樹後考慮 u u 節點所代表左括號和與之配對的右括號形成的區域中黑色塊的面積,記為 d p ( u ) dp(u) ,為了維護面積,多維護兩個值 h ( u ) , w ( u ) h(u),w(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)
進而有
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)
累加該森林每棵樹根節點的 d p dp 值即為答案,時間複雜度 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;
}