洛谷-P5658 [CSP-S 2019] 括号树 题解
<p>值域线段树 + 离线的 <span class="math inline">\(O(n\log n)\)</span> 做法。</p><h2 id="题目大意">题目大意</h2>
<p>给定一棵树,每个节点有一个括号。对于每个节点 <span class="math inline">\(i\)</span>,定义 <span class="math inline">\(s_i\)</span> 为从根节点到 <span class="math inline">\(i\)</span> 的路径上所有括号按顺序组成的字符串。求每个 <span class="math inline">\(s_i\)</span> 中互不相同的合法括号子串的个数 <span class="math inline">\(k_i\)</span>。</p>
<h2 id="思路">思路</h2>
<p>首先,<span class="math inline">\(k_i\)</span> 可以从父节点递推得到,<span class="math inline">\(k_i=k_{f_i}+a_i\)</span>。其中 <span class="math inline">\(a_i\)</span> 为以节点 <span class="math inline">\(i\)</span> 结尾的合法括号序列数量。因此只要求出每个节点的 <span class="math inline">\(a\)</span>。</p>
<p>经典技巧,将 <span class="math inline">\(\texttt{(}\)</span> 的权值设为 <span class="math inline">\(1\)</span>,<span class="math inline">\(\texttt{)}\)</span> 设为 <span class="math inline">\(−1\)</span> ,做树上前缀和。设点 <span class="math inline">\(u\)</span> 的前缀和为 <span class="math inline">\(sum_u\)</span>。则以 <span class="math inline">\(u\)</span> 结尾的合法括号子串的开头 <span class="math inline">\(v\)</span> 必须满足:</p>
<ol>
<li><span class="math inline">\(sum_{f_v}=sum_u\)</span>。</li>
<li>对于 <span class="math inline">\(v\to u\)</span> 这条链上的所有点 <span class="math inline">\(x\)</span>,有 <span class="math inline">\(sum_x\ge sum_u\)</span>。</li>
</ol>
<p>在 DFS 过程中开一棵值域线段树维护 <span class="math inline">\(1\to u\)</span> 这条链上每个 <span class="math inline">\(sum\)</span> 值对应的最大节点深度。这样就能找到 <span class="math inline">\(sum_p<sum_u\)</span> 且深度最大的节点 <span class="math inline">\(p\)</span>。</p>
<p>设 <span class="math inline">\(ask(x,y)\)</span> 表示 <span class="math inline">\(1\to x\)</span> 链上 <span class="math inline">\(sum=y\)</span> 的节点数量。则 <span class="math inline">\(a_u=ask(f_u,k)-ask(p,k)\)</span>。</p>
<p>第一遍 DFS 求出所有询问并离线下来。</p>
<p>第二遍 DFS 求出所有点的 <span class="math inline">\(a\)</span>。</p>
<p>第三遍 DFS 对 <span class="math inline">\(a\)</span> 做树上前缀和得到所有点的 <span class="math inline">\(k\)</span> 即可。</p>
<p>时间复杂度 <span class="math inline">\(O(n\log n)\)</span>。</p>
<h2 id="code">Code</h2>
<pre><code class="language-cpp">#include <bits/stdc++.h>
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define ls(p) ((p)<<1)
#define rs(p) ((p)<<1|1)
#define eb emplace_back
#define int long long
using namespace std;
constexpr int N=5e5+5;
struct Query{
int k,coef,id;
// k:目标值
// coef:贡献系数,1/-1
// id:贡献给到的节点
Query(int _k,int _coef,int _id):k(_k),coef(_coef),id(_id){}
};
struct SegTree{
int t;
void update(int p,int pl,int pr,int pos,int x){// 单点修改
if(pl==pr) return void(t=x);
int mid=pl+pr>>1;
if(pos<=mid) update(ls(p),pl,mid,pos,x);
else update(rs(p),mid+1,pr,pos,x);
t=max(t,t);
}
int query(int p,int pl,int pr,int l,int r){// 区间max
if(l>r) return 0;
if(l<=pl&&pr<=r) return t;
int mid=pl+pr>>1,a=0;
if(l<=mid) a=max(a,query(ls(p),pl,mid,l,r));
if(mid<r) a=max(a,query(rs(p),mid+1,pr,l,r));
return a;
}
}sgt;
char s;
int sum,dep,cnt,a,st;
int n,m,ans;
vector<int> g;
vector<Query> q;
void dfs1(int u){
int lst=sgt.query(1,1,m,sum,sum);
sgt.update(1,1,m,sum,dep);
st]=u;
for(int v:g){
sum=sum+(s=='('?1:-1);
dep=dep+1;
if(s==')'){
int bound=sgt.query(1,1,m,1,sum-1);
q.eb(sum,1,v);
if(bound) q].eb(sum,-1,v);
}
dfs1(v);
}
sgt.update(1,1,m,sum,lst);
}
void dfs2(int u){
++cnt];
for(Query x:q){
a+=x.coef*cnt;
}
for(int v:g) dfs2(v);
--cnt];
}
void dfs3(int u){
for(int v:g){
a+=a;
dfs3(v);
}
ans^=u*a;
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
cin>>n>>s+1;
m=n<<1;
rept(i,2,n){
int x;cin>>x;
g.eb(i);
}
g.eb(1);
sum=n,dep=1;// 为了不出负数,sum统一加上n
dfs1(0),dfs2(0),dfs3(0);
cout<<ans;
return 0;
}
</code></pre><br><br>
来源:https://www.cnblogs.com/xiaoniu142857/p/19258500
頁:
[1]