Newcoder 111 A.托米的簡單表示法(樹形)
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;
}