E. Mark and Professor Koro
题意

给定一个长度为n的数组,有q次更新操作,每次更新会将下标为k的元素的值更新为l,数组的更新是永久的。将数组更新后假设重复做以下操作,求可以得到的最大值。

  • 选择数组中两个相同的数x
  • 将这两个数从数组中删除
  • 将x+1加入到数组中

上述操作不改变原有的数组。

分析

将两个相同的数合并后值加1,可以联想到二进制加法 2 x + 2 x = 2 x + 1 2^x+2^x=2^{x+1} 2x+2x=2x+1,因此问题就转化为将每个数 a [ i ] a[i] a[i]都看作 2 a [ i ] 2^{a[i]} 2a[i],求将这些数相加后得到的二进制数最高位1的位数。将 a [ k ] a[k] a[k]修改为 l l l就转化为从原有的二进制数中减去 2 a [ k ] 2^{a[k]} 2a[k],再加上 2 l 2^l 2l,其中涉及到二进制借位进位。问题经过转化后需要进行的操作有单点修改,区间修改,查询最高位的1,考虑用线段树维护二进制每一位的状态。对于二进制加法,如果要加的位为0,直接加1即可,如果为1则需要找到比当前位更高的位中值为0且位数最低的一位,这就需要在线段树上进行二分,同理二进制减法当前位为0时也需要二分。查询二进制最高位的1同样需要二分,因此本题用到三次线段树二分。在线段树内二分的复杂度为 l o g ( n ) log(n) log(n),总时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))。

AC代码

#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N=2e5+100;
int a[N],b[N];
int n,q,mx,tot;struct Segment_Tree {int tr[N<<2],tag[N<<2];void addtag(int p,int l,int r,int d){tr[p]=(r-l+1)*d;tag[p]=d;}void pushdown(int p,int l,int r){if(tag[p]!=-1){int mid=(l+r)>>1;addtag(p<<1,l,mid,tag[p]);addtag(p<<1|1,mid+1,r,tag[p]);tag[p]=-1;}}void build(int p,int l,int r){tag[p]=-1;if(l==r){tr[p]=b[l];return ;}int mid=(l+r)>>1;build(p<<1,l,mid);build(p<<1|1,mid+1,r);tr[p]=tr[p<<1]+tr[p<<1|1];}void change(int p,int l,int r,int x,int y,int d){if(x<=l&&y>=r){tr[p]=(r-l+1)*d;tag[p]=d;return ;}pushdown(p,l,r);int mid=(l+r)>>1;if(x<=mid) change(p<<1,l,mid,x,y,d);if(y>mid)  change(p<<1|1,mid+1,r,x,y,d);tr[p]=tr[p<<1]+tr[p<<1|1];}int query(int p,int l,int r,int x,int y){if(x<=l&&y>=r) return tr[p];pushdown(p,l,r);int mid=(l+r)>>1;int ans=0;if(x<=mid) ans+=query(p<<1,l,mid,x,y);if(y>mid)  ans+=query(p<<1|1,mid+1,r,x,y);return ans;}int qry0(int p,int l,int r,int x,int z){if(l==r) return l;pushdown(p,l,r);int  mid=(l+r)>>1;if(x>mid) return qry0(p<<1|1,mid+1,r,x,z-tr[p<<1]);else{if(tr[p<<1]-z==mid-x) return qry0(p<<1|1,mid+1,r,x,z-tr[p<<1]);else return qry0(p<<1,l,mid,x,z);}}int qry1(int p,int l,int r,int x,int z){if(l==r) return l;pushdown(p,l,r);int  mid=(l+r)>>1;if(x>mid) return qry1(p<<1|1,mid+1,r,x,z-tr[p<<1]);else{if(tr[p<<1]-z==0) return qry1(p<<1|1,mid+1,r,x,z-tr[p<<1]);else return qry1(p<<1,l,mid,x,z);}}int qry(int p,int l,int r,int x){if(l==r) return l;pushdown(p,l,r);int mid=(l+r)>>1;if(tr[p<<1]==x) return qry(p<<1,l,mid,x);else return qry(p<<1|1,mid+1,r,x-tr[p<<1]);}
}seg;int main()
{ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin>>n>>q;for(int i=1;i<=n;i++){cin>>a[i];b[a[i]]++;mx=max(mx,a[i]);}int sum=0;for(int i=1;i<=mx;i++){b[i]+=sum;sum=b[i]/2;b[i]=b[i]%2;}int tot=mx;while(sum){b[++tot]=sum%2;sum/=2;}tot=200050;seg.build(1,1,tot);while(q--){int k,l;cin>>k>>l;if(!seg.query(1,1,tot,l,l)) seg.change(1,1,tot,l,l,1);else{int sum=seg.query(1,1,tot,1,l);int r=seg.qry0(1,1,tot,l,sum);seg.change(1,1,tot,l,r-1,0);seg.change(1,1,tot,r,r,1);}int x=a[k];if(seg.query(1,1,tot,x,x)) seg.change(1,1,tot,x,x,0);else{int sum=seg.query(1,1,tot,1,x);int r=seg.qry1(1,1,tot,x,sum);seg.change(1,1,tot,x,r-1,1);seg.change(1,1,tot,r,r,0);}a[k]=l;cout<<seg.qry(1,1,tot,seg.tr[1])<<endl;}return 0;
}

注意

在线段树外二分的复杂度为 l o g n 2 log_n^2 logn2​,总的时间复杂度为 O ( n l o g n 2 ) O(nlog_n^2) O(nlogn2​)

Codeforces Round #807 (Div. 2) E. Mark and Professor Koro(线段树二分)相关推荐

  1. Codeforces Round #807 (Div. 2) E. Mark and Professor Koro 二进制/线段树

    题目分析 模拟题目不难发现,实际上擦除操作就是在模拟二进制加法进位.那么可以得到原题意的转述: 将每个 a [ i ] a[i] a[i]看作 2 a [ i ] 2^{a[i]} 2a[i],维护整 ...

  2. Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake 线段树维护dp

    D. Babaei and Birthday Cake 题目连接: http://www.codeforces.com/contest/629/problem/D Description As you ...

  3. Codeforces Round #250 (Div. 1) D. The Child and Sequence 线段树 区间取摸

    D. The Child and Sequence Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest ...

  4. Codeforces Round #223 (Div. 2): E. Sereja and Brackets(线段树)

    题意: 给你一个括号序列和m次询问,每次询问区间[L, R]内匹配的括号个数 思路: 这道题线段树只用来维护区间最小值,所以理论上RMQ也可以,主要是要稍微推一下 设左括号为1,右括号为-1,s[]为 ...

  5. Codeforces Round #807 (Div. 2) A-D

    Codeforces Round #807 (Div. 2) A. Mark the Photographer 给定整数n,数组长度2*n,问能不能拆成两个长度为n的数组,两个数组的第i项有a[i]& ...

  6. Codeforces Round #807 (Div. 2)(A-D)

    Dashboard - Codeforces Round #807 (Div. 2) - CodeforcesCodeforces. Programming competitions and cont ...

  7. Codeforces Round #807 (Div. 2) A-C题解

    Codeforces Round #807 (Div. 2) A.B.C题题解 A - Mark the Photographer 题意:马克要给2n个人照相,分两排,一排站n人,给出每个人的身高,要 ...

  8. Codeforces Round #807 (Div. 2)A~E个人题解

    Dashboard - Codeforces Round #807 (Div. 2) - Codeforces A. Mark the Photographer 题意: 有个人,每个人的身高设为,现在 ...

  9. Codeforces Round #807 (Div. 2) A - D

    Codeforces Round #807 (Div. 2) 提交情况 参考 Codeforces Round #807 (Div. 2) A~E A. Mark the Photographer 标 ...

最新文章

  1. Matlab与数据结构 -- 求向量或矩阵的最大值
  2. js转Java的list_JS之JSON字符串到后台用Java转换成List实体类
  3. linux rz 上传文件夹_第二章Linux服务器环境搭建之Tomcat安装
  4. 【DD推荐】还用翻译工具给参数和方法取名?装个插件快速搞定!
  5. 阿里云 超级码力在线编程大赛初赛 第2场 题目4. 小栖的金字塔(超级卡特兰数+除法求模/乘法逆元)
  6. JZOJ1286太空电梯
  7. MINGW里面没有mingw32 make.exe
  8. 计算机笔记--【JVM】
  9. 三星t750c android6,BlueFrag:Android 蓝牙零交互远程代码执行漏洞分析 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com...
  10. UML统一建模实用教程 第六章 包图
  11. 或非门sr锁存器_sr锁存器的工作原理
  12. APP - 朋友圈广告原来是可以关掉的
  13. Win 10 + Ubuntu 18.04 双系统安装与深度学习环境配置安装踩坑实录(上篇)
  14. LTE物理信道PHICH--学习笔记
  15. 微积分 重点难点记录
  16. html全选替换,网站上如何实现全选与反选
  17. Linux之——UltraISO写入引导扇区时弹出“找到多于1个分区”解决方法
  18. java笔记10-多态
  19. 阿里旺旺服务器文件如何删除,电脑缓存如何清理 阿里旺旺如何清理内存缓存文件...
  20. web零基础学习笔记

热门文章

  1. 滴滴ElasticSearch千万级TPS写入性能翻倍技术剖析
  2. 中国商飞公司信息化计划处处长李云峰:企业数字化变革
  3. [COCI2006-2007#2] ABC
  4. 在 vue 中使用 window.onresize及div的resize事件
  5. 【转载】联想电脑无法重装系统原因 硬盘分区问题
  6. 微商引流微信要怎么设置?微商微信号怎么设置比较好?
  7. mac怎么格式化为ntfs?U盘格式化为ntfs格式
  8. 瓜娃之走马观花 (1) - List和Map
  9. 点在直线的投影坐标 n维向量投影坐标 几何投影坐标
  10. 向量投影与向量投影矩阵_Wlk1229-