Codeforces 题目传送门 & 洛谷题目传送门

一道线段树的套路题(似乎 ycx 会做这道题?orzorz!!11)

首先考虑什么样的 \(x\) 是“不合适”的,我们假设 \(a_1<a_2<a_3<\cdots<a_{n-1}<a_n\),显然如果 \(\exist k\) 使得 \(a_1+a_2+\cdots+a_k\le x<a_{n-k+1}+a_{n-k+2}+\cdots+a_{n}\),那么 \(x\) 就是不合法的。也就是说所有 \(x\) 可取的数组成的集合为 \(n\) 个形如 \([a_1+a_2+\cdots+a_k,a_{n-k+1}+a_{n-k+2}+\cdots+a_{n})\) 的区间的并,而我们要求的就是这个并集的大小。

直接求并集似乎不是特别容易,因为可能会存在区间有交,贡献不太容易直接计算(当然如果硬要算也是可以算的,不过分类讨论应该会下面的解法稍微复杂些)。我们不妨考虑正难则反,拿总个数减去“合适”的 \(x\) 的个数,记 \(A=\sum\limits_{i=1}^na_i\),那么在区间 \([0,A)\) 中,\(x\) 为“合适”的值当且仅当 \(\exist k\in[0,n-1]\) 满足 \(a_{n-k+1}+a_{n-k+2}+\cdots+a_n\le x<a_1+a_2+\cdots+a_k+a_{k+1}\),而显然这样的 \(n\) 个区间是不存在交集的,因此“合适的” \(x\) 的个数可以直接通过将这 \(n\) 个区间的长度相加求出,即 \(\sum\limits_{k=0}^{n-1}\max((a_1+a_2+\cdots+a_k+a_{k+1})-(a_{n-k+1}+a_{n-k+2}+\cdots+a_n),0)\)(因为可能存在区间为空,因此要对 \(0\) 取 \(\max\))。我们记 \(f(k)=(a_1+a_2+\cdots+a_k+a_{k+1})-(a_{n-k+1}+a_{n-k+2}+\cdots+a_n)\),那么 \(ans=\sum\limits_{k=0}^{n-1}\max(f(k),0)\),这边带个 \(\max\),不好直接算出,因此我们不妨来探究下 \(f\) 函数有什么性质,很容易发现 \(f(k)\) 满足以下两个性质:

  • \(f(k)=f(n-1-k)\)(提示:在左右两个括号同时加上 \(a_{k+2}+a_{k+3}+\cdots+a_{n-k}\))
  • \(\forall x<y\le\dfrac{n-1}{2}\),\(f(x)>f(y)\)(提示:对于 \(k<\dfrac{n-1}{2}\),\(f(k)-f(k-1)=a_{k+1}-a_{n-k+1}<0\))

第一个性质告诉我们可以计算前一半(\(k\le\dfrac{n-1}{2}\))的答案,然后乘个 \(2\),如果 \(n\) 是奇数再减去 \(\max(f(\dfrac{n-1}{2}),0)\) 即可计算求得最终答案。第二个性质告诉我们满足 \(f(k)>0,k<\dfrac{n-1}{2}\) 的 \(k\) 是一段前缀,可二分找到最大的满足 \(f(l)>0\) 的 \(l\),计算 \(\sum\limits_{i=0}^lf(i)\),这样就可以不带 \(\max\),直接计算了。

最后是就是怎样计算 \(f(l)\) 和一段前缀的 \(f(l)\) 的问题。考虑将 \(a\) 离散化并建权值线段树,然后可在权值线段树上二分在对数时间内找到全局第 \(k\) 大的值以及前 \(k\) 大的和。而 \(\sum\limits_{i=0}^lf(i)=\sum\limits_{i=0}^l(\sum\limits_{j=1}^{i+1}a_j-\sum\limits_{j=n-i+1}^na_j)=\sum\limits_{j=1}^{l+1}a_j(l+1-j)-\sum\limits_{n-l+1}a_j(j-n+l)\),在线段树上额外维护 \(\sum\limits_{i}a_ii\) 即可,这东西也可在 \(\log n\) 时间内求出。时间复杂度 \(n\log^2n\),因为在找最大的 \(f(l)>0\) 的 \(l\) 过程中需二分,可能可以用线段树二分等技巧把那个 \(\log\) 去掉,不过没想了(

最后,有人可能会担心运算过程中爆 long long,事实上虽然 \(\sum\limits_{i}a_ii\) 可能会爆 long long,但是答案是在 long long 范围内的,因此可以理解为系统像字符串哈希一样自动对 \(2^{64}\) 取模,而最后运算结果 \(<2^{64}\),因此取模得到的结果就是它本身,故直接用 long long 存是不会出问题的。

const int MAXN=2e5;
int n,qu,cnt=0,num=0;
ll key[MAXN*2+5],uni[MAXN*2+5],a[MAXN+5],tot=0;
struct event{int opt;ll x;} q[MAXN+5];
struct node{int l,r,cnt;ll sum,sum_i;} s[MAXN*8+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushup(int k){
s[k].cnt=s[k<<1].cnt+s[k<<1|1].cnt;s[k].sum=s[k<<1].sum+s[k<<1|1].sum;
s[k].sum_i=s[k<<1].sum_i+s[k<<1|1].sum_i+s[k<<1|1].sum*s[k<<1].cnt;
}
void modify(int k,int p,int x){
if(s[k].l==s[k].r){
s[k].cnt+=x;s[k].sum+=x*uni[p];
s[k].sum_i=1ll*s[k].cnt*(s[k].cnt+1)/2*uni[p];
return;
} int mid=s[k].l+s[k].r>>1;
if(p<=mid) modify(k<<1,p,x);
else modify(k<<1|1,p,x);
pushup(k);
}
pair<ll,ll> getxth(int k,int x){
if(s[k].l==s[k].r){return mp(1ll*x*uni[s[k].l],1ll*x*(x+1)/2*uni[s[k].l]);}
if(x<=s[k<<1].cnt) return getxth(k<<1,x);
else{
pair<ll,ll> ss=getxth(k<<1|1,x-s[k<<1].cnt);
return mp(ss.fi+s[k<<1].sum,ss.se+s[k<<1].sum_i+ss.fi*s[k<<1].cnt);
}
}
ll getf(int k){
pair<ll,ll> s1=getxth(1,k+1),s2=getxth(1,n-k);
return s1.fi-(s[1].sum-s2.fi);
}
ll getans(){
if(!n) return 0;
int l=0,r=(n-1)/2,p=(n-1)/2+1;
while(l<=r){
int mid=l+r>>1;
if(getf(mid)<=0) p=mid,r=mid-1;
else l=mid+1;
} p--;//printf("%d\n",p);
pair<ll,ll> s1=getxth(1,p+1),s2=getxth(1,n-p);
s2.fi=s[1].sum-s2.fi;s2.se=s[1].sum_i-s2.se;
// printf("%lld %lld %lld %lld\n",s1.fi,s1.se,s2.fi,s2.se);
ll sum=s1.fi*(p+2)-s1.se-s2.se+s2.fi*(n-p);
sum=sum*2;if((n&1)) sum-=max(getf((n-1)/2),0ll);
return tot-sum;
}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),key[++cnt]=a[i],tot+=a[i];
for(int i=1;i<=qu;i++){scanf("%d%lld",&q[i].opt,&q[i].x);key[++cnt]=q[i].x;}
sort(key+1,key+cnt+1);
for(int i=1;i<=cnt;i++) if(key[i]^key[i-1]) uni[++num]=key[i];
for(int i=1;i<=n;i++) a[i]=lower_bound(uni+1,uni+num+1,a[i])-uni;
for(int i=1;i<=qu;i++) q[i].x=lower_bound(uni+1,uni+num+1,q[i].x)-uni;
build(1,1,num);for(int i=1;i<=n;i++) modify(1,a[i],1);
printf("%lld\n",getans());
for(int i=1;i<=qu;i++){
if(q[i].opt==1) modify(1,q[i].x,1),++n,tot+=uni[q[i].x];
else modify(1,q[i].x,-1),--n,tot-=uni[q[i].x];
printf("%lld\n",getans());
}
return 0;
}

Codeforces 1500E - Subset Trick(线段树)的更多相关文章

  1. Buses and People CodeForces 160E 三维偏序+线段树

    Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...

  2. CodeForces 877E DFS序+线段树

    CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...

  3. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  4. [Codeforces 1199D]Welfare State(线段树)

    [Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...

  5. [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)

    [Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...

  6. Codeforces Gym 100231B Intervals 线段树+二分+贪心

    Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...

  7. Codeforces 482B Interesting Array(线段树)

    题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,如今有M个限制,每一个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 ...

  8. codeforces 383C Propagating tree 线段树

    http://codeforces.com/problemset/problem/383/C 题目就是说,  给一棵树,将一个节点的值+val, 那么它的子节点都会-val, 子节点的子节点+val. ...

  9. CodeForces 228D. Zigzag(线段树暴力)

    D. Zigzag time limit per test 3 seconds memory limit per test 256 megabytes input standard input out ...

随机推荐

  1. CentOS 用户与群组

    目录 1.用户管理 1.1.切换用户 1.2.添加用户 1.3.删除用户 1.4.修改用户 2.群组管理 2.1.查看群组 2.2.添加群组 2.3.删除群组 2.4.修改群组 1.用户管理 Linu ...

  2. Scrum Meeting 0501

    零.说明 日期:2021-5-1 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 整装待发,准备冲刺 ...

  3. Noip模拟54 2021.9.16

    T1 选择 现在发现好多题目都是隐含的状压,不明面给到数据范围里,之凭借一句话 比如这道题就是按照题目里边给的儿子数量不超过$10$做状压,非常邪门 由于数据范围比较小,怎么暴力就怎么来 从叶子节点向 ...

  4. sort方法和自定义比较器的写法

    摘要 在做一些算法题时常常会需要对数组.自定义对象.集合进行排序. 在java中对数组排序提供了Arrays.sort()方法,对集合排序提供Collections.sort()方法.对自定义对象排序 ...

  5. JVM:Hotspot虚拟机中的对象

    在HotSpot虚拟机中,对象在内存中存储的布局可以被分为3个区域:对象头(Header).实例数据(Instance data)和对齐填充(Padding).对象头包括两部分信息,第一部分存储自身的 ...

  6. 它说你的代码有 Bug「GitHub 热点速览 v.21.44」

    作者:HelloGitHub-小鱼干 本周热点上的榜单大多数提升工作效率的实用工具,像是一个 API 管理所有通知消息(包括推送.邮件-)的 notifire,再是高速解析 JSON 文件的 simd ...

  7. 数字在排序数组中出现的次数 牛客网 剑指Offer

    数字在排序数组中出现的次数 牛客网 剑指Offer 题目描述 统计一个数字在排序数组中出现的次数. class Solution: def GetNumberOfK(self, data, k): i ...

  8. copy-list-with-random-pointer leetcode C++

    A linked list is given such that each node contains an additional random pointer which could point t ...

  9. hdu 1166 敌兵布阵(简单线段树or树状数组)

    题意: N个工兵营地,第i个营地有ai个人. 三种操作: 1.第i个营地增加x个人. 2.第i个营地减少x个人. 3.查询第i个到第j个营地的总人数. 思路: 线段树or树状数组 代码:(树状数组) ...

  10. $.ajax、$.get和$.post方法成功,完成请求,错误或失败的回调

    一.$.get和$.post的不同    1.get通过url提交的,post是通过http消息实体提交的    2.get提交大小限制为2kb,post不限制    3.get提交会被缓存下来,有安 ...