题面

洛谷翻译

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. Myers差分算法的理解、实现、可视化

    作者:Oto_G QQ: 421739728 目录 简介 基础 差异的描述 好的差异比较 算法介绍 名词解释 两个定理 绘制编辑图 感谢 简介 本文章对Myers差分算法(Myers Diff Alg ...

  2. stm32f103ve+BH1750使用教程+oled(HAL库)

    1.硬件:BH1750模块+oled 2.代码:BH1750是标准的iic协议的外设,我这里单独有iic的文件,之后想要实现多个设备共用一个iic. BH1750.c 1 #include " ...

  3. 五种方式实现 Java 单例模式

    前言 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创建自 ...

  4. 让我们用Vue cli全家桶搭建项目

    一般项目都会用到这几个,这里不在详细介绍概念,只是简单的使用.一.搭建cli 1.事先安装好cnpm(淘宝镜像) npm install -g cnpm --registry=https://regi ...

  5. Elasticsearch 在地理信息空间索引的探索和演进

    vivo 互联网服务器团队- Shuai Guangying 本文梳理了Elasticsearch对于数值索引实现方案的升级和优化思考,从2015年至今数值索引的方案经历了多个版本的迭代,实现思路从最 ...

  6. NC16692 [NOIP2001]求先序排列

    NC16692 [NOIP2001]求先序排列 题目 题目描述 给出一棵二叉树的中序与后序排列.求出它的先序排列.(约定树结点用不同的大写字母表示,长度 ≤ 8). 输入描述 2行,均为大写字母组成的 ...

  7. D2C小练习

    前端智能化现状及未来展望 简介 DEsign: Sketch,Photoshop ,Figma 起源:微软2017年, Design to code code: 前端 核心原理 design----& ...

  8. labview入门到出家10(进阶)——CAN通讯

    ​          讲完串口,这边再讲一个labveiw工控程序中比较常用的CAN通讯吧.很久没有写过CAN通讯的程序了,网上一搜就是什么现场总线,控制器局域网总线,然后一堆复杂的协议.在这里还是一 ...

  9. centos 7系统安装

    1.打开VMware软件,点击创建虚拟机,默认选择,点击下一步 2.选择稍后安装,点击下一步 3.在Linux系统中选择CentOS 7 64位,点击下一步 4.选择好安装位置后,点击下一步 5.选择 ...

  10. Netty源码解读(三)-NioEventLoop

    先看看EventLoop类图 我们在Netty第二篇文章中的代码中,看到有多次用到eventLoop.execute()方法,这个方法就是EventLoop开启线程执行任务的关键,跟踪进去看看 // ...