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
*/

浙公网安备 33010602011771号