文章目录
Description
Solution
Part 1: 抽象为树上问题并初步解决
首先,我们对括号序列建出一棵树。接着,我们尝试将两种操作转化为树上的操作。
定义位置 i i i 的匹配点 p i p_i pi 为与第 i i i 个括号匹配的括号的位置,则考虑每一个满足 s i = s_i= si=
(
的 i i i 对应的 [ i , p i ] [i,p_i] [i,pi],那么这些区间两两要么不相交,要么有包含关系,我们就对这些区间建出一棵森林来,使得区间 A 包含区间 B 当且仅当 A 是 B 的祖先。特别的,如果建出的是一棵森林而不是一棵树,那么我们添加一个根,并将各个森林的根连到这里就好了(等价于加入区间 [ 0 , n + 1 ] [0,n+1] [0,n+1])。
先考虑一操作,令 (A),(B)
对应的两个节点分别是
u
,
v
u,v
u,v。首先,一个显然的观察是,
u
,
v
u,v
u,v 必然是某个点的两个连续的儿子。接着,注意到操作结束后,
v
v
v 对应的区间会变为 p(A)(B)q
中 A
与 B
之间的 ()
,而
u
u
u 对应的区间会由原先的 (A)
扩展到整个 (A()B)
,所以该操作在树上体现为——将
v
v
v 挂到
u
u
u 上(即钦定
v
v
v 的父亲节点为
u
u
u),然后再将
v
v
v 的各个儿子挂到
u
u
u 上。
再考虑二操作。同样地令涉及到的两个节点分别为 u , v u,v u,v。那么很显然的, u , v u,v u,v 也必然是某个点的连续两个儿子;而该操作在树上就体现为,交换 u , v u,v u,v 的顺序。从而,根据操作二,我们可以任意打乱树上任意点的任意儿子之间的顺序,于是接下来,我们就再也不需要考虑顺序的问题了。
从而,问题转化为: 给定一棵树,点带权,每次你可以选定任意两个点 u , v u,v u,v,在满足 u , v u,v u,v 的父亲相同的前提下,以 x w u + y w v xw_u+yw_v xwu+ywv 的代价将 v v v 及其儿子先后分别挂到 u u u 处。你需要求出,最少需要多少的代价才能将树修改成一条从根出发的链。
不难发现,我们可以自浅而深地对树进行操作,每次从该层中选出一个特殊点,并将当前层的除特殊点以外的所有节点全部通过若干次一操作下放到下一层,挂到特殊点的各个子树上。因此,问题的关键在于:对于每一层,如何选出最优的特殊点,以及如何安排 ⌈ \lceil ⌈ 最优的将该层的非特殊点下放到特殊点子树的 ⌋ \rfloor ⌋ 方案。
自浅而深地枚举每一层。令当前层上各个节点按点权从小到大排序后分别为 a 1 , a 2 , ⋯ , a k a_1,a_2,\cdots,a_k a1,a2,⋯,ak。下面,我们分类讨论 ( x , y ) = ( 0 , 1 ) , ( x , y ) = ( 1 , 0 ) , ( x , y ) = ( 1 , 1 ) (x,y)=(0,1),(x,y)=(1,0),(x,y)=(1,1) (x,y)=(0,1),(x,y)=(1,0),(x,y)=(1,1) 的情况( ( x , y ) = ( 0 , 0 ) (x,y)=(0,0) (x,y)=(0,0) 时答案显然为 0 0 0)。
Part 2: x=0,y=1
贪心地保留 a k a_k ak 即可。此时代价为 ∑ i = 1 k − 1 w a i \sum_{i=1}^{k-1} w_{a_i} ∑i=1k−1wai,然后将 a 1 , a 2 , ⋯ , a k − 1 a_1,a_2,\cdots,a_{k-1} a1,a2,⋯,ak−1 下放即可。
为了让时间复杂度正确,我们可以使用 multiset 维护。具体来说,我们预先用若干个 vector 存下每层中所有点的点权,然后建立一个初始为空的 mutiset。接着,我们从深度为 1 1 1(根节点的深度为 0 0 0)出发, 一直往更深处枚举,每次先在 multiset 中加入当前层,每次删去 multiset 中权值最大的点即可。注意,在此过程中,还要维护 multiset 中各个点的权值和。
时间复杂度线性对数。
Part 3: x=1,y=1
首先考虑,将 a 1 , a 2 , ⋯ , a k a_1,a_2,\cdots,a_k a1,a2,⋯,ak 合并到一起,每次以二者点权之和的代价合并时总代价的下界。不难发现,这个下界就是 ∑ i = 2 k ( w a 1 + w a i ) \sum_{i=2}^k (w_{a_1}+w_{a_i}) ∑i=2k(wa1+wai)。
假设选出了特殊点 a t a_t at,则分成两类考虑:
- 若 t = 1 t=1 t=1,那么 a t a_t at 就是该层点权最小的节点,将其他节点合并过来即可,此时总代价达到下界。
- 若 t ≠ 1 t \neq 1 t=1,那么我们可以将 a 2 , a 3 , ⋯ , a t − 1 , a t + 1 , a t + 2 , ⋯ , a k a_2,a_3,\cdots,a_{t-1},a_{t+1},a_{t+2},\cdots,a_k a2,a3,⋯,at−1,at+1,at+2,⋯,ak 合并到最小值,最后再将最小值合并到 a t a_t at 处。不难发现此时的总代价依然取到下界。
从而,无论钦定哪个特殊点,该层的代价不变。于是我们贪心地钦定 a t a_t at 为特殊点即可,因为这样可以将点权最大的点扣押在当前层以免它对接下来的层产生更多的贡献。
实现方法与 Part 1 类似,时间复杂度线性对数。
Part 4: x=1,y=0
本题的重难点。足足占了 64 64 64 分。
依然考虑将 a 1 , a 2 , ⋯ , a k a_1,a_2,\cdots,a_k a1,a2,⋯,ak 合并到一起的总代价下界,不难发现这个值是 w a 1 × ( k − 1 ) w_{a_1} \times (k-1) wa1×(k−1)。假设钦定的特殊点是 a t a_t at,那么总代价是 w a 1 × ( k − 2 ) + w a t w_{a_1} \times (k-2)+w_{a_t} wa1×(k−2)+wat。
- 若 t = 1 t=1 t=1,总代价显然取到下界。
- 若 t ≠ 1 t \neq 1 t=1,总代价可能取不到下界。
因为总代价会随着 t t t 的变化而变化,所以我们就无法套用 Part 2 的贪心了。那么,我们该怎么办呢?
Key Observation
我们要尽量让 a 1 , a k a_1,a_k a1,ak 下放下去(即 t ≠ 1 t \neq 1 t=1 且 t ≠ k t \neq k t=k),且在满足上述条件下,无论 t t t 选取何值均等价。
Prove
首先,我们可以发现, a 1 a_1 a1 存在优势。这是因为 a 1 a_1 a1 是点权很小的点,如果我们将它下放下去,那么它可能会使接下来看到的一些层的最小点权更小,从而使总代价更小。而 a 2 , a 3 , ⋯ , a t a_2,a_3,\cdots,a_t a2,a3,⋯,at 在这方面就比不过 a 1 a_1 a1 了。
接着,我们可以发现,从另一角度来看, a t a_t at 也存在优势。这是因为,只有链尾的点的权值不会对总代价产生贡献;而为了最小化总代价,我们必然会尝试把点权尽量大的节点下放到最后一层,从而使总代价更小。而 a 1 , a 2 , ⋯ , a t − 1 a_1,a_2,\cdots,a_{t-1} a1,a2,⋯,at−1 在这方面就比不过 a t a_t at 了。
然后,可以发现,考虑的角度只有上述两种。这是因为,第一个角度对应着最小化每层中的点权最小值(即最小化 w a 1 × ( k − 1 ) w_{a_1} \times (k-1) wa1×(k−1)),第二个角度对应着 t ≠ 1 t \neq 1 t=1 时的前述贡献式 w a 1 × ( k − 2 ) + w a t w_{a_1} \times (k-2)+w_{a_t} wa1×(k−2)+wat 中的 w a t w_{a_t} wat,不存在第三个角度了。
从而,根据上述推理,尽量把在某些方面拥有优势的 a 1 , a k a_1,a_k a1,ak 同时下放下去是最优决策。
最后,考虑证明,在满足上述要求(即 t ≠ 1 t \neq 1 t=1 且 t ≠ k t \neq k t=k)的前提下,无论 t t t 取何值均等价。不难发现,只要 a 1 , a k a_1,a_k a1,ak 均被下放,考虑即将看到的每一层,其中点权最小的点必定不会是 a 2 , a 3 , ⋯ , a t − 1 , a t + 1 , a t + 2 , ⋯ , a k − 1 a_2,a_3,\cdots,a_{t-1},a_{t+1},a_{t+2},\cdots,a_{k-1} a2,a3,⋯,at−1,at+1,at+2,⋯,ak−1 这些点,而且最后链尾的节点如果是这些点的话,将它替换为 a k a_k ak 后会更优。因此,任意 a i a_i ai 必然都会对总代价产生恰好 w a i w_{a_i} wai 的贡献,从而这些 t t t 就都是等价的了。
证毕。
为方便叙述,令第 d d d 层的节点数量为 s i z d siz_d sizd。我们考虑根据 s i z d siz_d sizd 的值去分类讨论:
- 若 s i z d = 1 siz_d=1 sizd=1,那么该层就是链尾的那一层,不会对答案产生任何贡献。
- 若 s i z d ≥ 3 siz_d \ge 3 sizd≥3,根据 Key Observation,随意钦定一个 t ∈ [ 2 , k − 1 ] t \in [2,k-1] t∈[2,k−1] 即可。
- 接下来考虑 s i z d = 2 siz_d=2 sizd=2 的情况。
不难发现,
s
i
z
d
=
2
siz_d=2
sizd=2 的时候只有两种抉择:下放
a
1
a_1
a1,或者下放
a
t
a_t
at。注意到每一层的
s
i
z
siz
siz 是事先固定好的 ,于是模拟退火即可 。
接着考虑保证正确性的多项式做法。
首先,可以发现, s i z d = 2 siz_d=2 sizd=2 的层必定前面的若干层,再加上第 n − 2 n-2 n−2 层(根节点的深度为 0 0 0)。这是因为 s i z i = s i z i − 1 + c n t i − 1 siz_i=siz_{i-1}+cnt_i-1 sizi=sizi−1+cnti−1( c n t i cnt_i cnti 表示初始第 i i i 层的节点数),而 c n t cnt cnt 只会在最后的若干层才会为 0 0 0,所以 s i z [ 1 , n − 1 ] siz[1,n-1] siz[1,n−1] 必定形如 { 2 , 2 , ⋯ , 2 , ≥ 3 , ≥ 3 , ⋯ , ≥ 3 , 2 , 1 } \{2,2,\cdots,2,\ge 3,\ge 3,\cdots,\ge 3,2,1\} {2,2,⋯,2,≥3,≥3,⋯,≥3,2,1}。
对于第 n − 2 n-2 n−2 层的那个 s i z i = 2 siz_i=2 sizi=2,直接枚举其 2 2 2 种可能的状态即可,而关键在于极长 2 2 2 前缀的处理。为方便叙述,令 s i z [ 1 , n − 1 ] siz[1,n-1] siz[1,n−1] 的极长 2 2 2 前缀为 s i z [ 1 , r ] siz[1,r] siz[1,r]。
不难发现,第 1 1 1 层至第 r r r 层的总贡献必定是这些层中所有节点的权值,扣去被下放到 r + 1 r+1 r+1 的那个节点的点权。于是,如果我们暴力枚举这个点的话,时间复杂度为 O ( n 2 log n ) O(n^2 \log n) O(n2logn)。无法通过本题。
为方便叙述,令第 1 1 1 至第 r r r 层的各个节点按点权从小到大排序分别是 a 1 , a 2 , ⋯ , a k a_1,a_2,\cdots,a_k a1,a2,⋯,ak,其中被下放到第 r + 1 r+1 r+1 层的点是 a t a_t at。那么,前 r r r 层的贡献就是 ∑ i = 1 , i ≠ t k w a i \sum_{i=1,i \neq t}^k w_{a_i} ∑i=1,i=tkwai,即 S − w a t S-w_{a_t} S−wat,其中 S = ∑ i = 1 k w a i S=\sum_{i=1}^k w_{a_i} S=∑i=1kwai。
Lemma
必然有 t = 1 t=1 t=1 或 t = k t=k t=k。
Prove
首先,明确节点 a 1 a_1 a1 具有使某层的最小点权更小的优势, a k a_k ak 具有做链尾节点不算入总代价的优势。
接着,考虑任意 a i ( 1 ≤ i < k ) a_i(1 \le i<k) ai(1≤i<k),不难发现,若在某种方案中将 a i a_i ai 作为了链尾节点,那么将其替换为 a k a_k ak 必定更优,这是因为不但 S − w a k < S − w a i S-w_{a_k}<S-w_{a_i} S−wak<S−wai(替换后前 r r r 层更优),而且 w a k w_{a_k} wak 的权值比 w a i w_{a_i} wai 大(将点权更大的点设定为了链尾,因此替换后做链尾更优),所以此时 t = k t=k t=k 严格优于 t = i t=i t=i。
然后,考虑任意 a i ( 1 < i ≤ k ) a_i(1<i \le k) ai(1<i≤k),不难发现,若在某种方案中 a i a_i ai 做了该层点权最小的点,那么将其替换为 a 1 a_1 a1 后不劣。这是因为,将 a i a_i ai 替换为 a 1 a_1 a1 所导致的前 r r r 层总代价的上涨,会在之后若干层的贪心决策过程中翻回来。比如 1000 − 3 + 3 k ≤ 1000 − 5 + 5 k ( k ≥ 1 ) 1000-3+3k \le 1000-5+5k(k \ge 1) 1000−3+3k≤1000−5+5k(k≥1),说的就是这个道理。
证毕。
综上所述,总时间复杂度线性对数,本题被完美解决。
话说 x = 1 , y = 0 x=1,y=0 x=1,y=0 似乎有线性做法,不过我看不懂 wjz 聚聚这一部分的题解(
Code
#include <bits/stdc++.h>
#define int long long
#define inf LONG_LONG_MAX
using namespace std;
const int maxl=800005;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
return s*w;
}
int n,x,y,res,sum,siz[maxl];char str[maxl];
vector<int> ve[maxl];multiset<int,greater<int> > s;
multiset<int,greater<int> >::iterator it;
namespace ducati{
int pos,v[maxl],sta[maxl];
void get_all_in(){
cin>>n>>x>>y;
for (int i=1;i<=(n<<1);i++) cin>>str[i];
for (int i=1;i<=n;i++) v[i]=read();
}
void init(){
int T=0;pos=0;
for (int i=1;i<=(n<<1);i++){
if (str[i]==40) sta[++pos]=(++T);
else ve[pos].push_back(v[sta[pos]]),pos--;
}
}
void get_new_str(){
int pos;
for (int i=1;i<=n+1;i++){
if (ve[i].size()!=1) {pos=i-1;break;}
}
for (int i=1;i<=n;i++) ve[i].clear();
for (int i=1;i<=n-pos;i++) v[i]=v[i+pos];
for (int i=1;i<=2*(n-pos);i++) str[i]=str[i+pos];
n-=pos;
}
void work_01(){
for (int i=1;i<n;i++){
for (auto x:ve[i]) s.insert(x),sum+=x;
int x=(*s.begin());res+=sum-x,sum-=x,s.erase(s.begin());
}
}
void work_11(){
for (int i=1;i<n;i++){
for (auto x:ve[i]) s.insert(x),sum+=x;
int x=(*s.begin()),y=(*(--(it=s.end())));
res+=y*(s.size()-2)+sum,sum-=x,s.erase(s.begin());
}
}
void work_10(){
int R=0,minv=inf,maxv=0,cnt=0;res=inf;siz[1]=ve[1].size();
for (int i=1;i<=n;i++){
if (i^1) siz[i]=siz[i-1]+ve[i].size()-1;
if (siz[i]==2&&R==i-1) R=i;
}
for (int i=1;i<=R;i++){
for (auto x:ve[i]) cnt++,sum+=x,minv=min(minv,x),maxv=max(maxv,x);
}
if (cnt==n) {res=sum-maxv;return;}
for (int type=0;type<=1;type++){
int tot=0;
if (!type) tot=sum-minv,s.insert(minv);
else tot=sum-maxv,s.insert(maxv);
for (int i=R+1;i<=n-2;i++){
for (auto x:ve[i]) s.insert(x);
it=s.end(),it--,tot+=(*it)*(s.size()-2);
it=s.begin(),it++,tot+=(*it);
s.erase(it);
}
tot+=min(*s.begin(),*(--s.end()));
res=min(res,tot),s.clear();
}
}
void solve(){
get_all_in(),init(),get_new_str(),init();
if (x==0&&y==1) work_01();
else if (x==1&&y==0) work_10();
else if (x==1&&y==1) work_11();
cout<<res<<endl;
}
}
signed main(){ducati::solve();return 0;}