Codeforces 1500E - Subset Trick(线段树)
一道线段树的套路题(似乎 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(线段树)的更多相关文章
- Buses and People CodeForces 160E 三维偏序+线段树
Buses and People CodeForces 160E 三维偏序+线段树 题意 给定 N 个三元组 (a,b,c),现有 M 个询问,每个询问给定一个三元组 (a',b',c'),求满足 a ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)
[Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...
- [Codeforces 1199D]Welfare State(线段树)
[Codeforces 1199D]Welfare State(线段树) 题面 给出一个长度为n的序列,有q次操作,操作有2种 1.单点修改,把\(a_x\)修改成y 2.区间修改,把序列中值< ...
- [Codeforces 316E3]Summer Homework(线段树+斐波那契数列)
[Codeforces 316E3]Summer Homework(线段树+斐波那契数列) 顺便安利一下这个博客,给了我很大启发(https://gaisaiyuno.github.io/) 题面 有 ...
- Codeforces Gym 100231B Intervals 线段树+二分+贪心
Intervals 题目连接: http://codeforces.com/gym/100231/attachments Description 给你n个区间,告诉你每个区间内都有ci个数 然后你需要 ...
- Codeforces 482B Interesting Array(线段树)
题目链接:Codeforces 482B Interesting Array 题目大意:给定一个长度为N的数组,如今有M个限制,每一个限制有l,r,q,表示从a[l]~a[r]取且后的数一定为q,问是 ...
- codeforces 383C Propagating tree 线段树
http://codeforces.com/problemset/problem/383/C 题目就是说, 给一棵树,将一个节点的值+val, 那么它的子节点都会-val, 子节点的子节点+val. ...
- CodeForces 228D. Zigzag(线段树暴力)
D. Zigzag time limit per test 3 seconds memory limit per test 256 megabytes input standard input out ...
随机推荐
- CentOS 用户与群组
目录 1.用户管理 1.1.切换用户 1.2.添加用户 1.3.删除用户 1.4.修改用户 2.群组管理 2.1.查看群组 2.2.添加群组 2.3.删除群组 2.4.修改群组 1.用户管理 Linu ...
- Scrum Meeting 0501
零.说明 日期:2021-5-1 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 整装待发,准备冲刺 ...
- Noip模拟54 2021.9.16
T1 选择 现在发现好多题目都是隐含的状压,不明面给到数据范围里,之凭借一句话 比如这道题就是按照题目里边给的儿子数量不超过$10$做状压,非常邪门 由于数据范围比较小,怎么暴力就怎么来 从叶子节点向 ...
- sort方法和自定义比较器的写法
摘要 在做一些算法题时常常会需要对数组.自定义对象.集合进行排序. 在java中对数组排序提供了Arrays.sort()方法,对集合排序提供Collections.sort()方法.对自定义对象排序 ...
- JVM:Hotspot虚拟机中的对象
在HotSpot虚拟机中,对象在内存中存储的布局可以被分为3个区域:对象头(Header).实例数据(Instance data)和对齐填充(Padding).对象头包括两部分信息,第一部分存储自身的 ...
- 它说你的代码有 Bug「GitHub 热点速览 v.21.44」
作者:HelloGitHub-小鱼干 本周热点上的榜单大多数提升工作效率的实用工具,像是一个 API 管理所有通知消息(包括推送.邮件-)的 notifire,再是高速解析 JSON 文件的 simd ...
- 数字在排序数组中出现的次数 牛客网 剑指Offer
数字在排序数组中出现的次数 牛客网 剑指Offer 题目描述 统计一个数字在排序数组中出现的次数. class Solution: def GetNumberOfK(self, data, k): i ...
- 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 ...
- hdu 1166 敌兵布阵(简单线段树or树状数组)
题意: N个工兵营地,第i个营地有ai个人. 三种操作: 1.第i个营地增加x个人. 2.第i个营地减少x个人. 3.查询第i个到第j个营地的总人数. 思路: 线段树or树状数组 代码:(树状数组) ...
- $.ajax、$.get和$.post方法成功,完成请求,错误或失败的回调
一.$.get和$.post的不同 1.get通过url提交的,post是通过http消息实体提交的 2.get提交大小限制为2kb,post不限制 3.get提交会被缓存下来,有安 ...