Description
一天,他正在为解析算术表达式的课程准备课件。 在课程的第一部分,他只想专注于解析括号。 他为他的学生发明了一个有趣的正确括号序列的几何表示,如下图所示:
几何表示的定义:
1.对于一个括号序列 ,我们定义 是 的几何表示形式,则 的表示是一个 的方块,高度为 ;
2.对于一个括号序列 , 的表示是由一个比 宽 个单位高 个单位的矩形包围 ,它的高度为 ;
3.对于两个括号序列 和 , 的几何表示形式为把 放置在 右边的一个单位,且高度为 和 的高度的较大值。其中+指的是字符串的连接符。
在完成课件后,托米老师开始玩他做好的图片。 他将图像的有限区域交替地涂成黑色和白色,使最外面的区域全部涂成黑色。 对于上面的例子,这个着色如下所示:
现在给你一个合法的括号序列。 请计算颜色为黑色的区域的面积。
Input
输入的第一行包含一个整数 ,表示指定测试用例的数量。
每个测试用例前面都有一个空白行。
每个测试用例由一个合法括号序列 组成。 每行只包含字符 和 。
Output
对于每个测试用例,输出一行包含一个整数,表示相应几何表示的黑色部分的面积。
Sample Input
2
((()))
(())(()(()))
Sample Output
10
20
Solution
将该合法括号序列看作一个森林的
序,左括号表示从当前节点走向一个新的儿子节点,右括号则表示从当前节点走向父亲节点,建树后考虑
节点所代表左括号和与之配对的右括号形成的区域中黑色块的面积,记为
,为了维护面积,多维护两个值
分别表示只考虑以
为根的子树中所有节点构成区域的高度和宽度,那么显然有转移
进而有
累加该森林每棵树根节点的
值即为答案,时间复杂度
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;
}