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