题号:CF1705E Mark and Professor Koro
来源:Codeforces Round #807 (Div. 2)

前言:

这是一个易想不易写的题。官方题解给出了bitset暴力和线段树维护 01 01 01 区间两种解法。我在考场上想到了线段树和set两种维护 01 01 01 区间的套路,由于set的码量、空间复杂度、时间复杂度都比线段树好,所以我果断写了set,然后就写挂了。

题意:

黑板上有 n n n 个数,两个相等的数 x x x 可以合成一个数 x + 1 x+1 x+1,你可以进行任意次这种操作,请问最终能得到的最大数是多少? 为了加强这个问题,你需要回答 q q q 次询问,每次询问会将初始时黑板上的某个数修改,这个修改在后面的询问中仍然有效,每次询问你都要回答此时能得到的最大数是多少。

数据范围:

分析:

显然, a i a_i ai​并不大,我们可以开个数组记录 a i a_i ai​的出现次数。合并两个数的过程类似于二进制中的进位,因此我们考虑用进位的思想将出现次数化简。我们从低位开始进位(从 1 1 1开始进位),若当前数 x 的出现次数大于 2 2 2,我们就向 x + 1 x+1 x+1 进位,代码类似于下面这样:

    for(int i=1;i<=n;i++){cin>>a[i];cnt[a[i]]++;mx=max(mx,a[i]); mx表示当前最大的数}for(int i=1;i<=mx;i++){int x=cnt[i]/2;cnt[i]&=1;    //对2取余cnt[i+1]+=x;  //进位if(i==mx and x>0) mx++;  //更新mx}

最终,我们的 c n t cnt cnt数组就是一个 01 01 01 串,或者说是一个二进制串。
那么我们怎么修改呢?
将 x 修改为 y,等价于 c n t ( x ) − 1 cnt(x)-1 cnt(x)−1, c n t ( y ) + 1 cnt(y)+1 cnt(y)+1。
例如, x = 2 x=2 x=2, y = 5 y=5 y=5,我们的当前串为 1101011 1101011 1101011,

1101011 − 10 = 1101001 1101011-10=1101001 1101011−10=1101001
1101001 + 10000 = 1111001 1101001+10000=1111001 1101001+10000=1111001
因此修改后的串就是 1111001 1111001 1111001.

在这样的一次加法中,我们可能会遇到进位,这等价与将一串连续的 111 111 111 变成 0 0 0,然后下一位设置为 1 1 1。
在因此减法中,我们可能会遇到借位,这等价于将一个 1 1 1置为 0 0 0,并将一串连续的 0 0 0变成一串 1 1 1。
这在线段树中是一个经典问题,不过,这个东西 s e t set set 也能做。

解法:

我们将所有连续的一串 1 1 1的(左边界,右边界)插入 s e t set set,利用右边界pai1序。

在位置x插入1:
low_bound找到 x 可能在的区间 ( l , r ) ( l, r) (l,r),如果 x x x 不在该区间内,说明 x x x 处为 0 0 0 ,因此我们插入一个 ( x , x ) (x, x) (x,x)。但是这个区间可能会其他区间相邻,因此我们检查一下,区间能否合并。如果 x x x 在区间 ( l , r ) (l, r) (l,r)内,那么从 x x x 开始直到 r r r 都会因为进位归零, r + 1 r+1 r+1会被置为 1 1 1,我们还需要检查一下 r + 1 r+1 r+1能否与下一个区间合并。如果 x x x 大于 l l l,那么 ( l , x ) (l, x) (l,x)会分裂成新的区间。

在位置x减1:
low_bound找到 x 可能在的区间$ ( l, r)$,如果 x x x 在该区间内,说明$ x$ 处为 1 1 1 ,因此我们将该区间分裂成两个小区间 ( l , x − 1 ) (l,x-1) (l,x−1) 和 ( x + 1 , r ) (x+1,r) (x+1,r)即可,如果分裂后的区间长度为 0 0 0,那就没必要插入 s e t set set了。如果 x x x 不在该区间内,那么 x x x需要向该区间借位,结果是 ( l , r ) (l,r) (l,r) 变成 ( l + 1 , r ) (l+1,r) (l+1,r), 得到了新的区间 ( x , l ) (x, l) (x,l)。这个新的区间可能会与前面的区间合并,需要检查。

代码:

由于我的区间是根据 r r r 排序的,因此 p a i r pair pair 的 f i r s t first first 是 r r r, s e c o n d second second是 l l l。

#include<bits/stdc++.h>
#define fst ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;int n,q,mx,cnt[210010],a[210010];
set<pair<int,int> >st;  //NOLINTvoid add(int x){auto it=st.lower_bound({x,0});if(it==st.end() or it->second>x){int l=x,r=x;vector<pair<int,int> >d;if(it!=st.end() and it->second==x+1){r=it->first;d.push_back(*it);}if(it!=st.begin()){it--;if(it->first==x-1){l=it->second;d.push_back(*it);}}for(auto i:d) st.erase(i);st.insert({r,l});}else{auto y=*it;int r=y.first+1,l=r;it++;if(it!=st.end()){if(it->second==r+1){r=it->first;st.erase(it);}}st.erase(y);if(x>y.second){st.insert({x-1,y.second});}st.insert({r,l});}
}
void del(int x){auto it=st.lower_bound({x,0});if(x>=it->second){auto y=*it;if(x>y.second) st.insert({x-1,y.second});if(x<y.first) st.insert({y.first,x+1});st.erase(y);}else{auto y=*it;int l=x,r=y.second-1;if(it!=st.begin()){it--;if(it->first==x-1){l=it->second;st.erase(it);}}st.erase(y);st.insert({r,l});if(y.second<y.first) st.insert({y.first,y.second+1});}
}
signed main(){fst;cin>>n>>q;for(int i=1;i<=n;i++){cin>>a[i];cnt[a[i]]++;mx=max(mx,a[i]);}for(int i=1;i<=mx;i++){int x=cnt[i]/2;cnt[i]&=1;cnt[i+1]+=x;if(i==mx and x>0) mx++;}int lf=0,len=0;for(int i=1;i<=mx+1;i++){if(cnt[i]){if(len==0) lf=i;len++;}else{if(len>0){st.insert({lf+len-1,lf});}len=0;}}while(q--){int k,l;cin>>k>>l;if(a[k]==l){cout<<mx<<"\n";continue;}del(a[k]);add(l);a[k]=l;auto x=st.end();x--;mx=x->first;cout<<mx<<"\n";}return 0;
}

[CF1705E Mark and Professor Koro]相关推荐

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

    E. Mark and Professor Koro 题意 给定一个长度为n的数组,有q次更新操作,每次更新会将下标为k的元素的值更新为l,数组的更新是永久的.将数组更新后假设重复做以下操作,求可以得 ...

  2. E. Mark and Professor Koro

    7.16 E. Mark and Professor Koro 大意就是两个x可以合成一个x+1,问给定一堆数,然后每一次操作 x d 令a[x]=d,然后求出当前数所能造出来的最大数. 方法就是维护 ...

  3. CF 807 E. Mark and Professor Koro(权值线段树)

    Problem - E - CodeforcesCodeforces. Programming competitions and contests, programming communityhttp ...

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

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

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

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

  6. [Professor麦]并发编程就该这么学(长文预警)

    二轮复盘并发编程!!文末有一些我学习并发编程的感受,不知道怎么入手的可以看看,正所谓传道授业解惑也,传递怎么学比知识更重要!欢迎在评论区和我交流讨论 什么是线程安全 当多个线程同时访问一个对象时,如果 ...

  7. opencv——pcb上寻找mark点(拟合椭圆的方法)

    #include "stdafx.h" // FitCircle.cpp : 定义控制台应用程序的入口 #include "cv.h" #include &qu ...

  8. IOS笔记 #pragma mark的用法

    简单的来说就是为了方便查找和导航代码用的. 下面举例如何快速的定位到我已经标识过的代码. #pragma mark 播放节拍器 - (void) Run:(NSNumber *)tick { //.. ...

  9. 两个点 定位_深圳Mark点定位的一般原理与步骤

    Mark点定位主要应用于被测物体幅面巨大,远远超过相机视野时(一般在检测PCB,或者大料盘).相比于传统的检测方法,Mark点定位可以大大提高检测效率,但是因为考虑到被检物体冷热缩放.刚体形变等原因, ...

最新文章

  1. C ++中线程的简单示例
  2. http、TCP/IP协议与socket之间的区别
  3. 蓝桥杯-9-3摩尔斯电码(java)
  4. python判断ip地址是否合法_python实现判断一个字符串是否是合法IP地址的示例
  5. 基于混合云存储系统的电影推荐引擎小结
  6. web流程设计器 工作流的 整合视频教程 activiti画图 SSM和独立部署
  7. 消息中间件学习总结(17)——MQ与RPC的区别和关联
  8. python是什么软件-python是什么软件
  9. 钉钉扫码登录web网站
  10. 【面试】造价工程师面试试题汇总
  11. y=asin(wx+φ)的对称中心_y=asin(wx+φ)怎么求
  12. 2018优秀讲师排行榜出炉,将受邀出席开发者大会!
  13. 昼夜系统-游戏中的时间
  14. php intval获取手机号,微信小程序获取微信绑定授权手机号getPhoneNumber
  15. 比 Xshell 还好用的 SSH 客户端神器!
  16. 如何使用Python轻松解决TSP问题(遗传算法)
  17. jpg格式图片压缩怎么弄?怎么把jpg图片压缩小?
  18. AI与大数据分析结合,就像给大象装上翅膀
  19. 互联网协会与IDGVC发布Web2.0 100(附名单)
  20. 停止不必要的Windows服务

热门文章

  1. 用表情开发“俏皮”编程语言
  2. 【Texstudio深色模式】
  3. Firebase iOS 远程配置教程
  4. recast 4 BuildContours
  5. 亿万富翁“枪下留人”悬疑
  6. C++的动态绑定(Boolan笔记第五周)
  7. Java 多态如何实现动态绑定
  8. 最近一些给力的语录!
  9. 用 java 获得 字体列表
  10. 通俗易懂的I2C协议