题面

洛谷翻译

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. 2.Tensor Shape《Pytorch神经网络高效入门教程》Deeplizard

            ,之后,我们张量和基础数据的形状酱油卷积运算来改变. 卷积改变了高度和宽度维度以及颜色通道的数量.

  2. JUnit 5 - Nested Test 内嵌测试

    本文地址:https://www.cnblogs.com/hchengmx/p/15158658.html 1. Nested用来解决什么问题 简单地说,Nested用来解决,随着Case越来越多,C ...

  3. 《SQL Server基础——SQL语句》

    SQL Server基础--SQL语句       一.创建和删除数据库: 1.创建数据库(默认化初始值) 格式: CREATE DATABASE 数据库名称 例如: CREATE DATABASE ...

  4. 在Visual Studio 2019中使用scanf报错C4996解决办法

    错误警告信息 错误C4996 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To ...

  5. 这不会又是一个Go的BUG吧?

    hello,大家好呀,我是小楼. 最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响. 出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点 ...

  6. 不同network中的两个docker容器

    1. 创建docker网络 docker network create --subnet 172.18.0.1/16 test docker network ls 2. 创建两个容器指定docker ...

  7. Sentiment analysis in nlp

    Sentiment analysis in nlp The goal of the program is to analysis the article title is Sarcasm or not ...

  8. 从Mpx资源构建优化看splitChunks代码分割

    背景 MPX是滴滴出品的一款增强型小程序跨端框架,其核心是对原生小程序功能的增强.具体的使用不是本文讨论的范畴,想了解更多可以去官网了解更多. 回到正题,使用MPX开发小程序有一段时间了,该框架对不同 ...

  9. this关键字、static关键字、方法的调用

    1.带有static关键字的方法,不可使用this关键字.因为其调用方法为类名.方法名(建议这种方式,调用不需要对象的参与),不存在对象. 2.实例方法调用必须有对象的存在,先创建对象,通过引用.的方 ...

  10. 解决方案:可以ping别人,但是别人不能ping我

    背景:我在写分布式爬虫项目时遇到了slave端无法ping通我的master,我的master可以ping通slave.我将master的防火墙关闭后slave可以ping了,但是这不是解决办法.于是 ...