[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;
}
posted @ 2022-04-20 14:07  PYWBKTDA  阅读(98)  评论(0编辑  收藏  举报