P9356 「SiR-1」Bracket 题解

发布时间 2023-05-28 17:46:32作者: -zyc-

P9356 「SiR-1」Bracket 题解

首先我们来先考虑一下如何计算一个给定的 \(f(s[1,n])\)

一般括号序列的题目都是比较套路的将 \(\texttt{(}\) 赋值为 \(1\),将 \(\texttt{)}\) 赋值为 \(-1\),然后求一下前缀和记为 \(sum_i\),那么一个括号序列是合法的当且仅当 \(\forall i\in[1,n],sum_i\ge 0\)

然后观察本题的操作可以发现由于操作二是任意地进行循环位移,所以我们最多进行一次操作二(因为任意两次操作二都可以合并成一次),那么我们接下来只用考虑需要进行几次操作一、什么时候进行操作二。

引理一:

如果 \(sum_n=0\) 那么我们一定可以通过最多一次操作二使得该序列合法。

证明:

我们记 \(sum_p=\min_{i=1}^{n}sum_i\),所以 \(\forall i\in[1,n]sum_i-sum_p\ge0\)

如果 \(sum_p\ge 0\),那么显然引理一得证,下面考虑 \(sum_p<0\) 的情况。

由于 \(sum_n=0\),因此 \(\sum_{i=p+1}^{n}a_i=sum_n-sum_p=-sum_p\),那么如果我们将 \(s[p+1,n]\) 移动到最前面,则新的 \(\{sum_i'\}\) 就满足:

\[\begin{cases} sum'_i=sum_{i+p}-sum_p\ge0,&i\in[1,n-p]\\ sum'_i=sum_{i-p}+sum_{n-p}\ge0,&i\in[n-p+1,n] \end{cases} \]

故此时的括号序列合法。综上所述引理一得证。

所以说对于一个括号序列 \(s[l,r]\),如果我们最后不管是否需要都进行一次操作二,那么他变成合法序列的操作数就是 \(|sum_r-sum_{l-1}|+1\),故本题要求的答案为:

\[ans=\sum_{l=1}^{n}\sum_{r=l}^{n}f(s[l,r])=\dfrac{n(n+1)}{2}+\sum_{l=1}^{n}\sum_{r=l}^{n}|sum_r-sum_{l-1}| \]

求这个式子的话可以分成两类,\(sum_r\ge sum_{l-1}\)\(sum_r<sum_{l-1}\),然后分别计算答案,这个可以用值域树状数组在 \(\mathcal O(n\log n)\) 的时间复杂度内完成,具体的,记 \(val_i,num_i\) 表示当前的 \(\sum_{l=1}^{r}sum_{l-1}[sum_{l-1}\le i]\)\(sum_{l=1}^{r}[sum_{l-1}\le i]\),然后对于以 \(r\) 结尾的区间的贡献就是:\((num_{sum_r}\times sum_r-val_{sum_r})+(val_{inf}-(num_{inf}-num_{sum_r})\times sum_r)\)

但是这一部分其实是可以在 \(\mathcal O(n)\) 的时间复杂度内完成的,因为我们关心的只是所有二元组 \((x,y)\)\(|sum_y-sum_x|\) 的和(\(x\in[0,n],y\in[0,n]\)),并不关心 \(x,y\) 的大小关系,因此我们可以直接按照 \(sum_i\) 的值从小到大排序,答案就是:

\[\sum_{i=0}^{n}(sum_i\times i- \sum_{j=0}^{i-1}sum_{j}) \]

这是可以 \(\mathcal O(n)\) 求解的,而由于 \(sum_i\in[-n,n]\),所以说可以桶排,排序复杂度也是 \(\mathcal O(n)\),故总复杂度 \(\mathcal O(n)\)

这一部分说完了,接下来只剩一个问题,求出不需要进行操作二的区间的个数 \(tot\)。那么最终的答案 \(res=ans-tot\)

引理二:

一个括号序列 \(s[1,n]\) 进行完操作一后不需要进行操作二的充要条件是 \(sum_n=\min_{i=1}^{n}sum_i\)\(\min_{i=1}^{n}sum_i\ge0\)

证明:

这里我们记 \(mn=\min_{i=1}^{n}sum_i\),进行完操作一之后序列长度为 \(m\)。首先容易想到一个贪心的策略:我们在进行操作一的时候,如果要加入 \(\texttt{(}\) 一定是将它放到序列最前面,否则将它放到序列最后面,这样我们才能让进行操作一之后的前缀和序列 \(\{sum'_i\}\) 的值尽可能的大,然后我们分两类来考虑。

第一种,当 \(mn\ge 0\) 时,这时我们只在序列后面加入 \(\texttt{)}\),并且最终 \(sum'_m=0\),而显然 \(sum'_i\ge sum'_m,i\in[n+1,m]\),因此当 \(mn\ge0\) 时该括号序列不需要进行操作二。

第二种,当 \(mn<0\) 时,这时候如果 \(sum_n\ge 0\) 显然需要进行操作二,而当 \(sum_n<0\) 时会在序列前方加入 \(-sum_n\)\(\texttt{(}\),那么就有 \(sum'_{i-sum_n}=sum_i-sum_n,i\in[1,n]\),而要保证 \(sum'_i\ge 0,i\in[1,m]\),故 \(sum_i-sum_n\ge 0,i\in[1,n]\),也即 \(mn-sum_n\ge 0\),因此只有当 \(sum_n=mn\) 时才不需要进行最后一次操作二。

综上所述,不需要进行操作二需要满足 \(sum_n=mn\or mn\ge 0\)

接下来我们考虑如何统计原题目中各个子序列中满足上述条件的序列的数量。由于上述条件涉及到区间最小值,所以说很容易想到用单调栈去维护,然后枚举右端点,将右端点不断向右移动。现在我们记 \(st\) 为一个维护 \(sum_i,i\in[0,r]\) 的单调栈,里面的值单调递增,\(st\) 里存的是下标,即 \(st_{top}=r\)

那么对于 \(mn=sum_n\) 这个条件,显然需要满足 \(l\in(st_{top-1},r]\),区间个数为 \(r-st_{top-1}\),复杂度 \(\mathcal O(n)\)

而对于 \(mn\ge 0\),我们需要分段考虑,总的个数为:

\[\sum_{i=1}^{top-1}\sum_{j=st_{i-1}}^{st_i}[sum_{st_i}\ge sum_j] \]

这个式子可以用值域树状数组维护,复杂度 \(\mathcal O(n\log n)\),但可以继续优化。

如果我们记 \(pre_i\) 为它插入单调栈时他前面的那一个数的下标,则如果 \(i\) 此时在单调栈中那么他对 \(r\) 这个右端点的答案的贡献就是 \(\sum_{j=pre_i+1}^{i}[sum_i\ge sum_j]\)。而又由于每一个值 \(i\in[1,n]\),他的 \(pre_i\) 的值只有一个,所以它的贡献也是唯一确定的,因此只要我们能 \(\mathcal O(n)\) 求出所有 \(\sum_{j=pre_i+1}^{i}[sum_i\ge sum_j]\) 就行了,而这个式子其实和我们维护单调栈的过程十分类似——维护单调栈是将 \([pre_i+1,i-1]\) 之间所有 \(sum_j\ge sum_i\)\(j\)\(pop\) 掉了,而 \(\sum_{j=pre_i+1}^{i}[sum_i\ge sum_j]=i-pre_i-\sum_{j=pre_i+1}^{i-1}[sum_i \ge sum_j]+\sum_{j=pre_i+1}^{i-1}[sum_i=sum_j]\),这样就可以轻松 \(\mathcal O(n)\) 去维护了,具体的:

\(val_i\)\(\sum_{j=pre_i+1}^{i-1}[sum_i\ge sum_j]\)\(ex_i=\sum_{j=pre_i}^{i-1}[sum_sum_j]\)\(S\) 为插入 \(i\) 时所有弹出栈的下标的集合,则:

\[val_i=\sum_{j\in S}val_j+1,ex_i=\sum_{j\in S}(ex_j+1)[sum_j=sum_i] \]

那么答案就是 \(\sum_{i=1}^{top-1}st_i-st_{i-1}-val_i+ex_i\),再稍微维护一下前缀和即可,如果不清楚的话可以看一下代码中的 tot[i]。这样我们就做到了 \(\mathcal O(n)\) 的时间复杂度。

好了到这里这道题就差不多结束了,这里有 \(\mathcal O(n\log n)\)代码\(\mathcal O(n)\)代码