[luogu8293]序列变换
对括号序列建树(虚拟一个根节点),则
- 限制:每个节点恰有一个儿子
- 操作2:交换一个节点的两个儿子(以下默认儿子间无序)
- 操作1:对于一对兄弟\(x,y\),将\(y\)及\(y\)所有儿子改为\(x\)的儿子
显然应从上到下使用操作\(1\),即保留该层一个权值并将其余权值下放到下一层
记权值从小到大依次为\(a_{1}\le a_{2}\le ...\le a_{m}\),则保留\(a_{k}\)的代价为\(\begin{cases}(m-2)a_{1}+a_{k}&X=1\\\sum_{i=1}^{m}a_{i}-a_{k}&Y=1\end{cases}\)
注意到每个\(a_{k}\)恰被保留一次,即\(\sum a_{k}\)为常数项,不妨去掉
-
若\((X,Y)\ne (1,0)\),整理式子后,\(a_{i}\)系数均非负,即可贪心取\(k=m\)
-
若\((X,Y)=(1,0)\),代价为\((m-2)a_{1}\),在\(m=1\)时系数为负,因此前者贪心不正确
由于树深度的连续性,每一层开始的\(m\)形如\(11...1\ |\ 22...2\ |\ \ge 3\ 21\)
-
第一段没有选择,直接减掉所有点权和即可
-
第二段没有贡献,并恰会剩下一个,不妨枚举为\(x\)
-
第三段可以归纳第\(i\)层(指该段中)下放的权值可以包含前\(i\)层和\(x\)的极值(最小和最大)
在此基础上,每层的最小值均取到下界,并且最后一层下放的即恰为两个极值
-
第四段仅有\(1\)产生贡献,显然下放两个数中的较大值,结合前者也已取到上界
使用set维护,时间复杂度为\(O(n\log n)\)
#include<bits/stdc++.h>
using namespace std;
#define N 400005
#define M 10000005
#define ll long long
int n,X,Y,x,a[N],b[N],tot[M],st[N];
char s[N<<1];vector<int>v[N];
namespace Subtask1{
int cnt;ll sum,ans;
set<int>S;set<int>::iterator it;
void main(){
for(int i=1;i<=n;i++){
for(int j:v[i])cnt++,sum+=b[j],S.insert(j);
ans+=sum;if (X)ans+=(ll)(cnt-2)*b[*S.begin()];
x=(*--S.end()),cnt--,sum-=b[x],S.erase(x);
}
if (!X){
for(int i=1;i<=n;i++)ans-=b[i];
}
printf("%lld\n",ans);
}
};
namespace Subtask2{
int cnt[N],vis[N];ll ans,K[N],B[N];
void main(){
cnt[1]=v[1].size();
for(int i=2;i<=n;i++)cnt[i]=cnt[i-1]+v[i].size()-1;
int pos=1;
while (cnt[pos]==1)ans-=b[v[pos++][0]];
if (pos<=n){
if (cnt[pos]==2){
vis[v[pos][0]]=vis[v[pos][1]]=1,pos++;
while (cnt[pos]==2)vis[v[pos++][0]]=1;
}
int mn=n+1,mx=0;
while (cnt[pos]!=1){
for(int i:v[pos])mn=min(mn,i),mx=max(mx,i);
K[mn-1]+=cnt[pos]-2,B[mn]+=(ll)(cnt[pos]-2)*b[mn];
pos++;
}
for(int i=n;i;i--)K[i]+=K[i+1];
for(int i=mx;i<=n;i++)K[i]--;
for(int i=1;i<=n;i++)B[i]+=B[i-1];
for(int i=1;i<mx;i++)B[i]-=b[mx];
ll s=B[n]-b[mx];
for(int i=1;i<=n;i++)
if (vis[i])s=min(s,K[i]*b[i]+B[i]);
ans+=s;
}
for(int i=1;i<=n;i++)ans+=b[i];
printf("%lld\n",ans);
}
};
int main(){
scanf("%d%d%d%s",&n,&X,&Y,s+1);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),tot[a[i]]++;
for(int i=1;i<M;i++)tot[i]+=tot[i-1];
for(int i=n;i;i--)b[tot[a[i]]]=a[i],a[i]=tot[a[i]]--;
for(int i=1,j=0;i<=(n<<1);i++){
if (s[i]=='(')st[++st[0]]=a[++j];
else v[st[0]].push_back(st[st[0]]),st[0]--;
}
if ((!X)&&(!Y))printf("0\n");
if (Y)Subtask1::main();
if ((X)&&(!Y))Subtask2::main();
return 0;
}