题解:CodeForce 1300E Subset Trick
1. Description
我们称一个数 \(x\) 对于一个集合 \(S\) 是不合适的,当且仅当存在两个大小相同的子集,满足其中一个子集的子集和 \(\le x\),而另一个子集的子集和 \(>x\)。
现在给出一个集合 \(S\) 和若干次增加,删除操作,询问刚开始以及每一次操作后,有多少个数 \(x\) 对现在的集合是不合适的。
2. Solution
首先是一个很显然的结论,如果令前 \(i\) 小的数的和为 \(mi_i\),前 \(i\) 大的数的和为 \(mx_i\),则不合适的 \(x\) 组成的集合是 \(\cup _{i=1}^{|S|} [mi_i,mx_i)\)。
这是很显然的,这里不作证明。
很显然也会发现,所有区间要么相交,要么相离,不会出现包含的情况,因为显然会有 \(mi_i\ge mi_{i-1},mx_i\ge mx_{i-1}\)。
此时存在结论:相交的区间对应的下标是一段连续的区间。
我们考虑证明这个结论。
proof
首先,两个区间相交,当且仅当 \(mi_i\le mx_{i-1}\)。
假定存在两个区间 \([l_1,r_1],[l_2,r_2]\) 满足 \(l_2<r_1\) 且 \(\forall i\in[l_1+1,r_1],mi_i\le mx_{i-1}\) 且 \(\forall i\in[l_2+1,r_2],mi_i\le mx_{i-1}\)。
则有 \(mi_{r_1}\le mx_{r_1-1}\),\(mi_{r_1+1}>mx_{r_1}\),所以 \(mi_{r_1+1}-mi_{r_1}> mx_{r_1}-mx_{r_1-1}\),也就是第 \(r_1+1\) 小的数比第 \(r_1\) 大的数大,而因为 \(mi_{l_2}>mx_{l_2-1},mi_{l_2+1}\le mx_{l_2}\),也就是第 \(l_2+1\) 小的数比第 \(l_2\) 大的数小,这显然是不可能出现的,因为第 \(r_1+1\) 小的数小于第 \(l_2+1\) 小的数,而第 \(r_1\) 大的数大于第 \(l_2\) 大的数。
所以 \([1,n]\) 会被分成三段,第一段 \(mi_i>mx_{i-1}\) 且第 \(i\) 小的数小于第 \(i-1\) 大的数,第二段 \(mi_i\le mx_{i-1}\),第三段 \(mi_i>mx_{i-1}\) 且第 \(i\) 小的数大于第 \(i-1\) 大的数。
显然可以使用二分找到分界点 \([l,r]\),表示 \([l,r]\) 这一段的区间相交,其余区间相离。
然后我们需要求出 \(\sum_{i=1}^{l-1} mx_i-mi_i+\sum_{i=r+1}^{|S|} mx_i-mi_i+mx_{r}-mi_{l}\)。
这个东西可以通过维护一棵平衡树来维护,在每一个节点维护区间内的数字个数,区间和,区间前缀和之和和区间后缀和之和。
当然,更进一步的,我们会发现 \(r=n-l\),同样尝试着证明这个结论。
显然有关系 \(mi_i=sum-mx_{n-i}\)。
题设条件 \(mi_i \le mx_{i-1}\) 可改写为:\(mi_i\le S-mi_{n-i+1}\)。
我们定义 \(f(i)=mi_i+mi_{n-i+1}\),也就是 \(f(i)-S\le 0\),很显然的,\(f(i)=f(n-i+1)\)。
所以不难得到极大区间 \([l,r]\) 就是 \(\forall i\in(l,r],f(i)-S\le 0\),又因为存在对称性,所以 \(l+1=n-r+1\),所以 \(l+r=n\)。
3. Code
/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const int N=2e5+5;
int n,m;
mt19937 ran(Name);
struct Treap{
#define Siz(x) (x==0?0:f[x].siz)
#define Sum(x) (x==0?0:f[x].sum)
int rt,num;
int ch[N<<1][2];
ll val[N<<1];
struct Node{
int siz;
ll sum,sumpre;
Node(int _siz=0,ll _sum=0,ll _sumpre=0){
siz=_siz;
sum=_sum,sumpre=_sumpre;
}
Node operator +(const Node &T)const{
return Node(siz+T.siz,sum+T.sum,sumpre+T.sumpre+T.siz*sum);
}
}f[N<<1];
unsigned int c[N<<1];
int New(ll _v=0){
int p=++num;
ch[p][0]=0,ch[p][1]=0;
val[p]=_v;
f[p]=Node(1,val[p],val[p]);
c[p]=ran();
return p;
}
void pushup(int p){
f[p]=Node(1,val[p],val[p]);
if(ch[p][0])f[p]=f[ch[p][0]]+f[p];
if(ch[p][1])f[p]=f[p]+f[ch[p][1]];
}
int merge(int x,int y){
if(x==0&&y==0)return 0;
if(x==0)return y;
if(y==0)return x;
if(c[x]<c[y]){
ch[x][1]=merge(ch[x][1],y);
pushup(x);
return x;
}else{
ch[y][0]=merge(x,ch[y][0]);
pushup(y);
return y;
}
}
pii split_val(int p,ll v){
if(!p)return {0,0};
if(val[p]<=v){
pii tmp=split_val(ch[p][1],v);
ch[p][1]=tmp.first;
pushup(p);
return {p,tmp.second};
}else{
pii tmp=split_val(ch[p][0],v);
ch[p][0]=tmp.second;
pushup(p);
return {tmp.first,p};
}
}
pii split_siz(int p,int sz){
if(!p)return {0,0};
if(Siz(ch[p][0])>=sz){
pii tmp=split_siz(ch[p][0],sz);
ch[p][0]=tmp.second;
pushup(p);
return {tmp.first,p};
}else{
pii tmp=split_siz(ch[p][1],sz-Siz(ch[p][0])-1);
ch[p][1]=tmp.first;
pushup(p);
return {p,tmp.second};
}
}
void insert(ll v){
pii tmp=split_val(rt,v-1);
rt=merge(tmp.first,merge(New(v),tmp.second));
return ;
}
void erase(ll v){
pii tmp1=split_val(rt,v-1);
pii tmp2=split_val(tmp1.second,v);
rt=merge(tmp1.first,tmp2.second);
}
pair<ll,ll> query(int p,int k){
if(k<=Siz(ch[p][0]))return query(ch[p][0],k);
k-=Siz(ch[p][0]);
if(k<=1)return {Sum(ch[p][0])+val[p],val[p]};
k--;
pair<ll,ll> tmp=query(ch[p][1],k);
return {Sum(ch[p][0])+val[p]+tmp.first,tmp.second};
}
pair<ll,ll> querymi(int k){
return query(rt,k);
}
pair<ll,ll> querymx(int k){
pair<ll,ll> tmp=query(rt,f[rt].siz-k+1);
return {f[rt].sum-tmp.first+tmp.second,tmp.second};
}
ll __querypresum(int p,int k){
if(k<=Siz(ch[p][0]))return __querypresum(ch[p][0],k);
ll res=0;
k-=Siz(ch[p][0]);
res+=ch[p][0]?f[ch[p][0]].sumpre+k*f[ch[p][0]].sum:0;
if(k<=1)return res+val[p];
k--;
res+=val[p]*(k+1);
return res+__querypresum(ch[p][1],k);
}
ll querypresum(int l,int r){
if(l>r)return 0;
ll res;
if(r==n)res=f[rt].sumpre;
else res=__querypresum(rt,r);
if(l!=1)res-=__querypresum(rt,l-1);
return res;
}
int size(){
return Siz(rt);
}
ll querysum(int L){
int n=f[rt].siz;
ll sum1=querypresum(1,L);
ll sum2=querypresum(n-L+1,n);
return 1ll*(2*L+1)*f[rt].sum-2ll*sum1-2ll*sum2;
}
}treap;
ll query(){
int n=treap.size();
if(n<=1)return 0;
int l,r,L,R;
l=2,r=n-1,L=n;
while(l<=r){
int mid=l+r>>1;
pair<ll,ll> pre=treap.querymi(mid);
pair<ll,ll> suf=treap.querymx(mid-1);
if(pre.first<=suf.first){
L=mid;
r=mid-1;
}else if(pre.second<suf.second)l=mid+1;
else r=mid-1;
}
if(L==n)return 1ll*(n+1)*treap.f[treap.rt].sum-2ll*treap.f[treap.rt].sumpre;
L--;
R=n-L;
return treap.querysum(L);
}
signed main(){
read(n),read(m);
for(int i=1;i<=n;i++){
ll x;
read(x);
treap.insert(x);
}
write(query()),Nxt;
for(int i=1,opt;i<=m;i++){
ll x;
read(opt),read(x);
if(opt==1)treap.insert(x);
else treap.erase(x);
write(query()),Nxt;
}
}

浙公网安备 33010602011771号