[省选联考 2022] 序列变换 题解
神奇贪心。
改变一下操作的形态,发现就是括号树上两个节点 \(a,b\),把 \(b\) 和 \(b\) 的所有儿子挂到 \(a\) 上。
有一个显然的贪心策略是从上到下合并,这样可以在不改变上层的选择的情况下让下层的选择更多。
观察到只有四种情况。开始分讨。
x=0,y=0
憨憨题。
x=0,y=1
每次合并代价是 \(b\) 的代价。那么每一层留下一个最大的就行了。开个 multiset 存权值,算一下就行了。
x=1,y=1
每次合并是 \(a+b\) 的代价。发现只要把除了最大值以外的值合到最小值上,再把最小值合到最大值上就行了。每一层的权值是(个数 \(-2\))乘最小值再加一个所有的和。
上边所有的加起来有 32 分。
x=1,y=0
每次合并是 \(a\) 的代价。考虑以下我们的策略:如果每次把所有的合到最小的(剩下一个节点),然后把最小的合到剩下的节点下面,代价就是(个数 \(-2\))乘最小值再加上剩下的节点的权值。然而容易发现随着向下合并每次都要算上一个节点的代价,那么最后只有一个节点可以不算代价。显然把最大值和最小值都下放到最底层是最优的。
然后考虑一些情况:
- 只有一个儿子。没法下放,直接跳过。
- 大于两个儿子。下放最大值和最小值。
- 只有两个儿子。不是很好搞。
发现只有两个儿子的一定可以形成一段最前面的连续段(跳过 \(1\) 后的)。那么对这些连续段内的元素讨论。发现只有两种情况是最优的:把这一段的最小值放到底层,或者把最大值放到底层。那么模拟两种情况,选个最小的即可。
意外的好写。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
using namespace std;
int n,x,y,a[400010];
long long ans,sum;
char ch[800010];
multiset<int>s;
vector<int>g[400010];
int top,cnt,stk[400010],size[400010],mx[400010],mn[400010];
int main(){
scanf("%d%d%d%s",&n,&x,&y,ch+1);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
if(x==0&&y==0){
puts("0");return 0;
}
for(int i=1;i<=(n<<1);i++){
if(ch[i]=='(')stk[++top]=++cnt;
else{
g[top].push_back(a[stk[top]]);top--;
}
}
if(x==0&&y==1){
for(int i=1;i<n;i++){
for(int x:g[i])s.insert(x),sum+=x;
int ret=*prev(s.end());sum-=ret;
ans+=sum;
s.erase(prev(s.end()));
}
}
if(x==1&&y==1){
for(int i=1;i<n;i++){
for(int x:g[i])s.insert(x),sum+=x;
int mn=*s.begin();ans+=1ll*mn*(s.size()-2);
ans+=sum;sum-=*prev(s.end());
s.erase(prev(s.end()));
}
}
if(x==1&&y==0){
size[0]=1;
for(int i=1;i<=n;i++)size[i]=size[i-1]+g[i].size()-1,mn[i]=0x3f3f3f3f;
int pos1=1,pos2=1;
while(pos1<=n&&size[pos1]==1)pos1++;
pos2=pos1;
while(pos2<=n&&size[pos2]==2)pos2++;
for(int i=pos1;i<n;i++){
if(i!=pos2)mn[i]=mn[i-1],mx[i]=mx[i-1];
for(int x:g[i])mn[i]=min(mn[i],x),mx[i]=max(mx[i],x),sum+=x;
}
long long ans1=sum-mx[n-1],ans2=sum-max(mx[pos2-1],mx[n-1]);
for(int i=pos2;i<n;i++){
ans1+=1ll*(size[i]-2)*min(mn[pos2-1],mn[i]);
ans2+=1ll*(size[i]-2)*mn[i];
}
ans=min(ans1,ans2);
}
printf("%lld\n",ans);
return 0;
}
快踩