Loading

CF1895G

CF1895G

平衡树小练习(虽然应该也可以用别的数据结构)。

题意

有长度为 $n$ 的 $01$ 串。需要给每个字符上红色或者蓝色,上色后会获得对应的权值。最后所有红色的逆序对需要扣除 $1$ 的权值。最大化权值。

$n\le 4\times 10^5$。

Sol

二选一染色并获得价值很容易想到最小割。刻画逆序对的权值也是容易的,具体来讲是对于所有 $0$ 从源点连 $b_i$,并连到汇点 $r_i$。$1$ 则反过来。之后对于每个 $1$ 都往后的 $0$ 点连一条流量为 $1$ 的边。

点数是 $\mathcal{O}(n)$ 但是边数是 $\mathcal{O}(n^2)$ 的。而且似乎也没有更多能狗优化逆序对的刻画方法了。

下文设 $k=r_i-\min(r_i,b_i)$。

观察这个网络流图,发现其实可以直接模拟其跑最大流的过程。首先遍历每个点,每个点一定能够先通过 $k$ 的流量。之后分两种点考虑:

  • $1$ 点回多出来 $k$ 的流量给后面的 $0$ 点。
  • $0$ 点回从前面拿至多 $k$ 的流量,对于之前每个 $1$ 点只能拿 $1$ 的流量(前提是其还有额外的流量)。

对于拿取额外流量,贪心地思考可以发现,如果前面有多余 $k$ 个 $1$ 点能够提供额外流量,那么一定选择额外流量最大的 $k$ 个(能够证明如果选少的能做到的值选多的一定也能做到)。发现其实我们需要维护的是当前还有多少 $1$ 点还有额外流量,然后要对其进行一个 $-1$ 操作。更具体的,我们维护一个集合:

  • 可以进行插入元素。
  • 可以查询元素个数。
  • 可以剔除掉 $=0$ 的元素。
  • 可以对于最大的 $k$ 个元素全体 $-1$。

此时平衡树看起来是一个不错的选择。修改一下 FHQTreap 的一些操作即可,建议自己尝试构建,能够提升对于平衡树的理解。

代码缩进有点小问题,建议直接去 CF 提交记录

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fi first
#define se second
mt19937 Rnd(random_device{}());
const int maxn=5e5+10;
struct FHQTreap{
    #define ls(x) ch[x][0]
    #define rs(x) ch[x][1]
    int siz[maxn],val[maxn],tg[maxn],ch[maxn][2],pri[maxn],tnod,root,w[maxn];
    void cl(){root=0;siz[root]=tg[root]=ls(root)=rs(root)=val[root]=w[root]=0;}
    void PU(int x){
        siz[x]=w[x];
        if(ls(x))siz[x]+=siz[ls(x)];
        if(rs(x))siz[x]+=siz[rs(x)];
    }
    void PD(int x){
        if(ls(x))val[ls(x)]-=tg[x],tg[ls(x)]+=tg[x];
        if(rs(x))val[rs(x)]-=tg[x],tg[rs(x)]+=tg[x];
        tg[x]=0;
    }
    int newnode(int x,int t){
        if(!t)return 0;
        assert(t>=0);
        ++tnod;
        val[tnod]=x;
        siz[tnod]=w[tnod]=t;
        tg[tnod]=0;
        pri[tnod]=Rnd();
        return tnod;
    }
    int morge(int u,int v){
        if(!u||!v)return u+v;
        if(pri[u]<pri[v]){
            PD(u),rs(u)=morge(rs(u),v),PU(u);
            return u;
        } 
        PD(v),ls(v)=morge(u,ls(v)),PU(v);
        return v;
    }
    void split(int now,int k,int &u,int &v){
        if(!now)u=v=0;
        else{
            PD(now);
            if(val[now]<=k)u=now,split(rs(now),k,rs(now),v);
            else {
                v=now;
                split(ls(now),k,u,ls(now));
            }
            PU(now);
        }
    }
    int chkdel(int x){
        int u=0,v=0;
        split(x,0,u,v);
        return v;
    }
    int findk(int now,int k,int t){
        if(!now)return -1;
        PD(now);
        if(val[now]==k){
            w[now]+=t,siz[now]+=t;
            return now;
        }
        int res;
        if(val[now]<k)res=findk(rs(now),k,t);
        else res=findk(ls(now),k,t);
        PU(now);
        return res;
    }
    void ins(int &now,int x,int t){
        if(!t)return;
        int u,v;
        if(findk(now,x,t)==-1){
            split(now,x,u,v);
            now=morge(morge(u,newnode(x,t)),v);
        }
    }
    pair<int,int> lkth(int now,int k){//which node and the size of that node
        while(1){
            PD(now);
            if(siz[rs(now)]>=k)now=rs(now);
            else{
                if(k>=siz[rs(now)]+1&&k<=siz[rs(now)]+w[now]){
                    return make_pair(val[now],w[now]);
                }
                k-=siz[rs(now)]+w[now],now=ls(now);
            }
        }
    }
    int rk(int x){//the rank of the last x
        int res,u=0,v=0;
        split(root,x,u,v);
        res=siz[v]+1;
        morge(u,v);
        return res;
    }
    int gmin(int x){
        PD(x);
        if(!ls(x))return x;
        return gmin(ls(x));
    }
    int gmax(int x){
        PD(x);
        if(!rs(x))return x;
        return gmax(rs(x));
    }
    int Q(){return siz[root];}
    void delkmax(int k){
        pair<int,int> V=lkth(root,k);
        int R2=rk(V.fi),R=R2+V.se-1;//R ---- R2
        int u,v,w;
        split(root,V.fi-1,u,v);
        split(v,V.fi,w,v);
        ins(u,V.fi,R-k),ins(v,V.fi,k-R2+1);
        tg[v]++,val[v]--,v=chkdel(v);
        if(!v){root=u;return;}
        int Vmin=gmin(v),Umax=gmax(u);
        while(val[Vmin]<=val[Umax]){
            split(v,val[Vmin],w,v);
            ins(u,val[Vmin],siz[w]);
            if(!v)break;
            Umax=gmax(u),Vmin=gmin(v); 
        }
        root=morge(u,v);
    }
    void delall(){
        tg[root]++,val[root]--;
        root=chkdel(root);
    }
}mytp;
int T,n;
char s[maxn];
int r[maxn],b[maxn],ans=0,sig;
signed main(){
    srand(time(0));
    cin>>T;
    while(T--){
        cin>>n;
        scanf("%s",s+1);
        ans=0,sig=0;
        mytp.cl();
        for(int i=1;i<=n;i++)cin>>r[i],sig+=r[i];
        for(int i=1;i<=n;i++)cin>>b[i],sig+=b[i];
        for(int i=1;i<=n;i++){
            int minval=min(r[i],b[i]);
            ans+=minval;
            if(r[i]==minval)continue;
            if(s[i]=='1'){
                mytp.ins(mytp.root,r[i]-minval,1);
            } else {
                if(mytp.Q()>r[i]-minval){ 
                    mytp.delkmax(r[i]-minval),ans+=r[i]-minval;
                }
                else ans+=mytp.Q(),mytp.delall();
            }
        }
        cout<<sig-ans<<"\n";
    }
    return 0;
}
/*
1
10
0 1 1 0 0 1 1 0 0 0
0 7 2 5 7 1 6 0 3 6 
5 3 0 9 7 5 1 2 1 1 
X O O X X X O X O O
*/
posted @ 2023-11-14 07:10  Jryno1  阅读(11)  评论(0)    收藏  举报  来源