题面

洛谷翻译

V

a

n

y

a

\rm Vanya

Vanya 有一个初始大小为

n

n

n 的集合

S

S

S 和

q

q

q 次往集合加数/删数的操作。(集合中每个数字不同)

称一个数

x

x

x 为不合适的,当前仅当满足可以取出

S

S

S 的两个大小相同的子集,其中一个元素和

x

\le x

≤x,另一个元素和

>

x

>x

>x。

求初始集合与每次操作结束后的集合不合适

x

x

x 的数量

n

,

q

2

e

5

,

S

i

1

e

13

n,q\leq 2e5,S_i\leq 1e13

n,q≤2e5,Si​≤1e13

3000 ms , 512 mb

题解

洛谷的翻译已经转换了一部分题意了,我们接着转换。

既然是两个大小相同的子集,那么针对每种子集大小,我们不妨找出元素和最小以及元素和最大的两个集合,令其元素和分别为

L

,

R

L,R

L,R,那么

[

L

,

R

)

[L,R)

[L,R) 以内都是不合适的

x

x

x 。

我们把整个集合从小到大排成一列,会发现问题简单了许多。元素和最小的大小为

i

i

i 的集合就是前缀

i

i

i(

s

u

m

(

i

)

sum(i)

sum(i)),同理,元素和最大的大小为

i

i

i 的集合就是后缀

S

i

+

1

|S|-i+1

∣S∣−i+1(

s

u

f

(

S

i

+

1

)

suf(|S|-i+1)

suf(∣S∣−i+1))。

回过头来,我们可以把要求的答案表示为

[

L

1

,

R

1

)

[

L

2

,

R

2

)

.

.

.

[

L

S

,

R

S

)

[L_1,R_1)\cup[L_2,R_2)\cup...\cup[L_{|S|},R_{|S|})

[L1​,R1​)∪[L2​,R2​)∪...∪[L∣S∣​,R∣S∣​),即

[

s

u

m

(

1

)

,

s

u

f

(

S

)

)

[

s

u

m

(

2

)

,

s

u

f

(

S

1

)

)

.

.

.

[

s

u

m

(

S

)

,

s

u

f

(

1

)

)

[sum(1),suf(|S|))\cup[sum(2),suf(|S|-1))\cup...\cup[sum(|S|),suf(1))

[sum(1),suf(∣S∣))∪[sum(2),suf(∣S∣−1))∪...∪[sum(∣S∣),suf(1)) ,我们会发现一些规律:

  1. 由于序列是单增的,因此

    y

    =

    s

    u

    m

    (

    x

    )

    y=sum(x)

    y=sum(x) 下凸,

    y

    =

    s

    u

    f

    (

    S

    x

    +

    1

    )

    y=suf(|S|-x+1)

    y=suf(∣S∣−x+1) 上凸。

  2. s

    u

    m

    (

    S

    )

    =

    s

    u

    f

    (

    1

    )

    sum(|S|)=suf(1)

    sum(∣S∣)=suf(1) ,因此

    [

    s

    u

    m

    (

    S

    )

    ,

    s

    u

    f

    (

    1

    )

    )

    [sum(|S|),suf(1))

    [sum(∣S∣),suf(1)) 是空集,我们把它去掉。

  3. s

    u

    m

    (

    i

    )

    =

    S

    u

    m

    S

    s

    u

    f

    (

    i

    +

    1

    )

    ,

    s

    u

    f

    (

    S

    i

    +

    1

    )

    =

    S

    u

    m

    S

    s

    u

    m

    (

    S

    i

    )

    sum(i)={\rm Sum_S}-suf(i+1),suf(|S|-i+1)={\rm Sum_S}-sum(|S|-i)

    sum(i)=SumS​−suf(i+1),suf(∣S∣−i+1)=SumS​−sum(∣S∣−i) ,因此

    [

    L

    i

    ,

    R

    i

    ]

    [L_i,R_i]

    [Li​,Ri​] 和

    [

    L

    S

    i

    ,

    R

    S

    i

    ]

    [L_{|S|-i},R_{|S|-i}]

    [L∣S∣−i​,R∣S∣−i​] 是大小相等的,整个并集是左右对称的。

我们会发现,难点就在于这是集合并,不能简单地求长度和,我们得判断哪些区间融合了,哪些单了出来。好在之前的规律可以帮助我们,我们可以发现一个规律:整个并集两边的区间单出来一些(possibly, none),中间则是一个大区间,也就是存在中间一段

[

L

s

,

R

s

)

.

.

.

[

L

t

,

R

t

)

[L_s,R_s)\cup...\cup[L_t,R_t)

[Ls​,Rs​)∪...∪[Lt​,Rt​) 是一整个大区间,其余的

{

[

L

i

,

R

i

)

i

<

s

i

>

t

}

\{[L_i,R_i) |i<s\vee i>t\}

{[Li​,Ri​)∣i<s∨i>t} 都是独立的(长度可以直接加)。形似这样:

[)  [-)    [---)     [-----------------------------------)     [---)    [-)  [)
  • 证明:由于并集对称,我们只证明左半边,右半边同理。考虑左半边某个区间

    [

    s

    u

    m

    (

    i

    )

    ,

    s

    u

    f

    (

    S

    i

    +

    1

    )

    )

    [sum(i),suf(|S|-i+1))

    [sum(i),suf(∣S∣−i+1)) 是独立的,那么

    s

    u

    f

    (

    S

    i

    +

    1

    )

    <

    s

    u

    m

    (

    i

    +

    1

    )

    suf(|S|-i+1)<sum(i+1)

    suf(∣S∣−i+1)<sum(i+1) ,也即

    s

    u

    f

    (

    S

    (

    i

    1

    )

    +

    1

    )

    +

    S

    S

    i

    +

    1

    <

    s

    u

    m

    (

    i

    )

    +

    S

    i

    +

    1

    (1)

    suf(|S|-(i-1)+1)+S_{|S|-i+1}<sum(i)+S_{i+1}\tag{1}

    suf(∣S∣−(i−1)+1)+S∣S∣−i+1​<sum(i)+Si+1​(1)
    由于是在序列左半边,序列从左到右单增,因此

    S

    i

    +

    1

    <

    S

    S

    i

    +

    1

      


      

    S

    S

    i

    +

    1

    <

    S

    i

    +

    1

    (2)

    S_{i+1}<S_{|S|-i+1}\;\Leftrightarrow\;-S_{|S|-i+1}<-S_{i+1}\tag{2}

    Si+1​<S∣S∣−i+1​⇔−S∣S∣−i+1​<−Si+1​(2)
    由于同号,因此把这个不等式

    (

    2

    )

    (2)

    (2) 和上面的不等式

    (

    1

    )

    (1)

    (1) 左右两边分别相加,可得:

    s

    u

    f

    (

    S

    (

    i

    1

    )

    +

    1

    )

    <

    s

    u

    m

    (

    (

    i

    1

    )

    +

    1

    )

    suf(|S|-(i-1)+1)<sum((i-1)+1)

    suf(∣S∣−(i−1)+1)<sum((i−1)+1)
    所以我们发现区间

    [

    L

    i

    1

    ,

    R

    i

    1

    )

    =

    [

    s

    u

    m

    (

    i

    1

    )

    ,

    s

    u

    f

    (

    S

    (

    i

    1

    )

    +

    1

    )

    )

    [L_{i-1},R_{i-1})=[sum(i-1),suf(|S|-(i-1)+1))

    [Li−1​,Ri−1​)=[sum(i−1),suf(∣S∣−(i−1)+1)) 也就是它左边那个区间也是独立的。
    由此可得,左边的独立区间一定是最靠左的连续一段

    [

    L

    i

    ,

    R

    i

    )

    [L_i,R_i)

    [Li​,Ri​) ,由于对称,右边也一样。证毕。

我们每次计算答案可以只算左半边,就能得到右半边的答案,同时要处理最中间的特殊情况。我们先考虑求区间独立的范围

[

1

,

s

)

[1,s)

[1,s) ,这个可以二分位置,然后判断

s

u

m

sum

sum 和

s

u

f

suf

suf 的大小关系。我们得到

s

s

s 过后,

t

t

t 就可以直接对称得到,中间的大区间就很好计算了,我们找到右端点左端点做个减法就行。对于左边的独立区间,答案就是

i

=

1

s

1

(

R

i

L

i

)

=

i

=

1

s

1

s

u

f

(

S

i

+

1

)

i

=

1

s

1

s

u

m

(

i

)

=

i

=

1

s

1

(

s

i

)

S

S

i

+

1

i

=

1

s

1

(

s

i

)

S

i

\sum_{i=1}^{s-1} (R_i-L_i)=\sum_{i=1}^{s-1} suf(|S|-i+1)-\sum_{i=1}^{s-1} sum(i)\\ =\sum_{i=1}^{s-1}(s-i)S_{|S|-i+1}-\sum_{i=1}^{s-1}(s-i)S_{i}

i=1∑s−1​(Ri​−Li​)=i=1∑s−1​suf(∣S∣−i+1)−i=1∑s−1​sum(i)=i=1∑s−1​(s−i)S∣S∣−i+1​−i=1∑s−1​(s−i)Si​

综上所述,我们得用数据结构维护一个单增序列每段子串的

S

i

\sum S_i

∑Si​,

S

i

i

\sum S_i\cdot i

∑Si​⋅i ,以及同时,该数据结构还得支持

  • 从某个值后面添加一个数
  • 删除某个值的数

那么最适合的数据结构就是平衡树了!虽然有点卡常。

官解做法:

推理结论都差不多,只不过角度不同:官解是用全集 - 空隙,然后证明了最左和最右两端的空隙都是连续的(定义了

f

(

k

)

=

s

u

m

(

k

+

1

)

s

u

f

(

n

k

+

1

)

f(k)=sum(k+1)-suf(n-k+1)

f(k)=sum(k+1)−suf(n−k+1),然后直接告诉你它在左半边是单减的(Let’s note two simply things: …))。

然后也是二分边界,维护元素和以及元素带权和,只不过用的是离散化+线段树,常数小一些,但是自我感觉想起来很麻烦。

CODE

无旋Treap ,中间求sumsuf时卡了卡常。
无O2: 2979 ms ,加O2: 2542 ms

#include<map>
#include<queue>
#include<ctime>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
#define eps 1e-9
//#pragma GCC optimize(2)
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
LL a[MAXN];
//----------------------------------Treap
struct np{int s[2];np(){s[0]=s[1]=0;}np(int A,int B){s[0]=A;s[1]=B;}};
struct tr{
int s[2],siz,hp,lz;
LL ky,sm,k2,sm2;
tr(){s[0]=s[1]=siz=hp=0;ky=sm=k2=sm2=lz=0;}
}tre[MAXN<<1];
int CNT;
inline int newnode(LL key) {
int x = ++ CNT; tre[x] = tr();
tre[x].ky = tre[x].sm = tre[x].k2 = tre[x].sm2 = key;
tre[x].siz = 1; tre[x].hp = rand() *1ll* rand() % 998244353;
return x;
}
inline int update(int x) {
int ls = tre[x].s[0],rs = tre[x].s[1];
tre[x].sm = tre[x].ky + tre[ls].sm + tre[rs].sm;
tre[x].sm2 = tre[x].k2 + tre[ls].sm2 + tre[rs].sm2 + (tre[ls].sm + tre[rs].sm) *1ll* tre[x].lz;
tre[x].siz = tre[ls].siz + tre[rs].siz + 1;
tre[0] = tr(); return x;
}
inline void addtm(int x,int y) {
if(!x) return ;
tre[x].k2 += tre[x].ky *1ll* y;
tre[x].sm2 += tre[x].sm *1ll* y;
tre[x].lz += y; return ;
}
inline void pushdown(int x) {
if(!x) return ;
int ls = tre[x].s[0],rs = tre[x].s[1];
if(tre[x].lz) {
addtm(ls,tre[x].lz); addtm(rs,tre[x].lz);
tre[x].lz = 0;
}return ;
}
inline np spli1(int x,LL k) {
np as(0,0); if(!x) return as;
pushdown(x);
int d = (tre[x].ky <= k ? 1:0);
as = spli1(tre[x].s[d],k);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x); return as;
}
inline np spli2(int x,int rk) {
np as(0,0); if(!x) return as;
pushdown(x); if(rk <= 0) return np(0,x);
int d = (tre[tre[x].s[0]].siz+1 <= rk ? 1:0);
if(d) rk -= tre[tre[x].s[0]].siz+1;
as = spli2(tre[x].s[d],rk);
tre[x].s[d] = as.s[d^1];
as.s[d^1] = update(x); return as;
}
inline int merg(int p1,int p2) {
if(!p1 || !p2) return p1+p2;
pushdown(p1); pushdown(p2);
if(tre[p1].hp < tre[p2].hp) {tre[p1].s[1] = merg(tre[p1].s[1],p2);return update(p1);}
tre[p2].s[0] = merg(p1,tre[p2].s[0]); return update(p2);
}
inline int ins(int x,int y) {
np p = spli1(x,tre[y].ky);
addtm(y,tre[p.s[0]].siz); addtm(p.s[1],1);
return merg(merg(p.s[0],y),p.s[1]);
}
inline int del(int x,LL k) {
np p1 = spli1(x,k-1);
np p2 = spli2(p1.s[1],1);
if(p2.s[0] && tre[p2.s[0]].ky == k) p2.s[0] = 0,addtm(p2.s[1],-1);
return merg(p1.s[0],merg(p2.s[0],p2.s[1]));
}
inline LL sum(int x,int y) {
if(!x || !y) return 0ll;
if(tre[tre[x].s[0]].siz+1 <= y)
return tre[tre[x].s[0]].sm+tre[x].ky+sum(tre[x].s[1],y-(tre[tre[x].s[0]].siz+1));
return sum(tre[x].s[0],y);
}
inline LL suf(int x,int y) {
if(!x || y > tre[x].siz) return 0ll;
if(tre[tre[x].s[0]].siz+1 < y)
return suf(tre[x].s[1],y-(tre[tre[x].s[0]].siz+1));
return tre[tre[x].s[1]].sm+tre[x].ky+suf(tre[x].s[0],y);
}
inline LL sum2(int &x,int y) {
np p1 = spli2(x,y);
LL res = tre[p1.s[0]].sm * (1ll+y) - tre[p1.s[0]].sm2;
x = merg(p1.s[0],p1.s[1]);
return res;
}
inline LL suf2(int &x,int y) {
np p1 = spli2(x,y-1);
LL res = tre[p1.s[1]].sm2 - tre[p1.s[1]].sm *1ll* (y-1ll);
x = merg(p1.s[0],p1.s[1]);
return res;
}
// ----------------------------------------------------------------
int root = 0;
inline LL calcu() {
n = tre[root].siz;
if(!root || n <= 0) return 0ll;
int md = n/2,st = 0;
LL ans = 0;
for(int i = 19;i >= 0;i --) {
int ad = st+(1<<i);
if(ad < md && suf(root,n-ad+1) < sum(root,ad+1)) st = ad;
}
if(st) ans += (suf2(root,n-st+1) - sum2(root,st)) * 2ll;
if((n-1) & 1) {
int rr = 2*md-st-1;
LL l1 = sum(root,st+1), r1 = suf(root,n-rr+1);
ans += r1 - l1;
}
else if(n > 1) {
int rr = 2*md-st;
LL l1 = sum(root,st+1),r1 = suf(root,n-md+1);
LL l2 = sum(root,md+1),r2 = suf(root,n-rr+1);
if(r1 < l2) ans += r1-l1 + r2-l2;
else ans += r2 - l1;
}
return ans;
}
int main() {
n = read(); m = read();
for(int i = 1;i <= n;i ++) {
a[i] = read();
root = ins(root,newnode(a[i]));
}
printf("%lld\n",calcu());
for(int i = 1;i <= m;i ++) {
o = read();
LL nm = read();
if(o == 1) root = ins(root,newnode(nm));
else root = del(root,nm);
printf("%lld\n",calcu());
}
return 0;
}

官解线段树jly的代码

[CF1500E] Subset Trick (平衡树)的更多相关文章

  1. Codeforces 420D Cup Trick 平衡树

    Cup Trick 平衡树维护一下位置. #include<bits/stdc++.h> #include <bits/extc++.h> #define LL long lo ...

  2. Codeforces 1500E - Subset Trick(线段树)

    Codeforces 题目传送门 & 洛谷题目传送门 一道线段树的套路题(似乎 ycx 会做这道题?orzorz!!11) 首先考虑什么样的 \(x\) 是"不合适"的,我 ...

  3. codeforces 420D Cup Trick

    codeforces 420D Cup Trick 题意 题解 官方做法需要用到线段树+平衡树(? 如果数据小的话似乎可以用莫队).然后代码好长好长.我补了一个只要用到树状数组的做法. 代码 #inc ...

  4. SGU - 507 启发式合并维护平衡树信息

    题意:给定一颗树,每个叶子节点\(u\)都有权值\(val[u]\),求每个非叶子节点子树的最小叶子距离,若该子树只有一个叶子节点,输出INF 貌似本来是一道树分治(并不会)的题目,然而可以利用平衡树 ...

  5. 平衡树(Splay、fhq Treap)

    Splay Splay(伸展树)是一种二叉搜索树. 其复杂度为均摊\(O(n\log n)\),所以并不可以可持久化. Splay的核心操作有两个:rotate和splay. pushup: 上传信息 ...

  6. 平衡树 & LCT

    1. 非旋 Treap(FHQ Treap) 1.1. 算法简介 FHQ Treap 的功能非常强大.它涵盖了 Treap 几乎所有的功能 所以我非常后悔学了 Treap,浪费时间. FHQ 的核心思 ...

  7. Pytorch技法:继承Subset类完成自定义数据拆分

    我们在<torch.utils.data.DataLoader与迭代器转换>中介绍了如何使用Pytorch内置的数据集进行论文实现,如torchvision.datasets.下面是加载内 ...

  8. EEG preprocessing - A Trick Before Doing ICA

    EEGLab maillist My ICs don't have high power in low frequency is b/c I do a small trick here. before ...

  9. [LeetCode] Partition Equal Subset Sum 相同子集和分割

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

随机推荐

  1. Java常用类-包装类

    包装类 ​ Java中的基本类型功能简单,不具备对象的特性,为了使基本类型具备对象的特性,所以出现了包装类,就可以像操作对象一样操作基本类型数据;包装类不是为了取代基本数据类型,而是在数据类型需要使用 ...

  2. 第六章、PXE高效网络装机、Kickstart无人值守安装

    目录 一.部署PXE远程安装服务 1PXE定义 2PXE服务优点 3搭建网络体系前提条件 4PXE实现过程讲解 二.搭建PXE远程安装服务器 三.Kickstart无人值守安装 一.部署PXE远程安装 ...

  3. BUUCTF-ningen

    ningen 从16进制看可以发现其中有压缩包,存在着504b0304,使用binwalk分离即可 压缩包带密码,根据提示是四位纯数字 使用ARCHPR破解即可

  4. 解开XAML的邪恶面纱

    什么是XAML,首先我们看下它的外观 <Window x:Class="Blend_WPF.WindowStyle"        xmlns="http://sc ...

  5. 使用vue实现排序算法演示动画

    缘起 最近做的一个小需求涉及到排序,界面如下所示: 因为项目是使用vue的,所以实现方式很简单,视图部分不用管,本质上就是操作数组,代码如下: { // 上移 moveUp (i) { // 把位置i ...

  6. CompletableFuture的入门

    runAsync 和 supplyAsync runAsync接受一个Runable的实现,无返回值 CompletableFuture.runAsync(()->System.out.prin ...

  7. CF141E Clearing Up 题解

    思路分析 自认为是一道很好的思维题. 直接看上去的想法是: 跑一个生成树,每一次加的边颜色交替进行,直到拉出生成树. 仔细想想,发现可能无法保证最后是一棵树而不是森林,也是说输出都是 \(-1\) . ...

  8. java.Scanner 拓展用法

    package study5ran2yl.study; import java.util.Scanner; public class demo11 { public static void main( ...

  9. ooday07 Java_接口

    笔记: 接口: 是一种引用数据类型 由interface定义 只能包含常量和抽象方法------默认权限是public 接口不能被实例化 接口是需要被实现/继承,实现/派生类:必须重写所有抽象方法 一 ...

  10. V.Internet基础及应用