Description
一天,他正在为解析算术表达式的课程准备课件。 在课程的第一部分,他只想专注于解析括号。 他为他的学生发明了一个有趣的正确括号序列的几何表示,如下图所示:
几何表示的定义:
1.对于一个括号序列 A A A,我们定义 g ( A ) g(A) g(A)是 A A A的几何表示形式,则 “ ( ) " “()" “()"的表示是一个 1 ∗ 1 1*1 1∗1的方块,高度为 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的高度的较大值。其中+指的是字符串的连接符。
在完成课件后,托米老师开始玩他做好的图片。 他将图像的有限区域交替地涂成黑色和白色,使最外面的区域全部涂成黑色。 对于上面的例子,这个着色如下所示:
现在给你一个合法的括号序列。 请计算颜色为黑色的区域的面积。
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) (1≤T≤10,∣s∣≤4⋅105)
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)=v∈son(u)max{h(v)}+1,w(u)=∣son(u)∣+1+v∈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)
dp(u)=h(u)⋅w(u)−v∈son(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;
}