CF1705E
题面
给定一个序列,可抽取其中两个相同的数
题解
你好。
我打这题真是糖丸了。
首先可以发现操作与序列顺序无关,只与各个元素的个数有关,于是可以用桶存每个元素的个数。
然后发现这个合并可以视作一棵二叉树(森林也可以)。
由数值最低的节点一层一层往上合并,下一层每两个子节点合并在这一层创造一个父节点并向这个父节点连边。所以可以把它看作一个高精度二进制数(因为二进制数是逢二进一),然后我们要求的就是这个二进制数的位数。
如果上面这一段看不懂可以感性理解一下(联系那个很火的合成大西瓜)。
可以在数据读入完之后把这个二进制数预处理出来。
发现每次操作会使得一个元素个数减少,一个元素个数增加,这就是这个二进制数的加减操作。
具体地,
直接高精度模拟二进制加减法,你会获得 Time limit exceeded on test 7。
于是要用数据结构维护这个高精度二进制数。发现如果整个序列都是
所以就可以用线段树维护,用前面的下标存低位。由于每次都是特定位
然后我们要求的答案是最后一个
我线段树存的是第一个
时间复杂度
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+55;
int n,q,a[N],cnt[N];
int ans[N],w;
struct segment {
int l,r;
int f[2],e[2],tag;
} s[N*4];
inline void init() {
int jw=0;
for(int i=1,res;i<=w;i++) {
res=ans[i]+jw;
ans[i]=res%2,jw=res/2;
}
while(jw) ans[++w]=jw%2,jw/=2;
return;
}
inline void pushup(int k) {
s[k].f[0]=min(s[k*2].f[0],s[k*2+1].f[0]),s[k].f[1]=min(s[k*2].f[1],s[k*2+1].f[1]);
s[k].e[0]=max(s[k*2].e[0],s[k*2+1].e[0]),s[k].e[1]=max(s[k*2].e[1],s[k*2+1].e[1]);
return;
}
inline void pushdown(int k) {
swap(s[k*2].f[0],s[k*2].f[1]),swap(s[k*2+1].f[0],s[k*2+1].f[1]);
swap(s[k*2].e[0],s[k*2].e[1]),swap(s[k*2+1].e[0],s[k*2+1].e[1]);
s[k*2].tag^=1,s[k*2+1].tag^=1;
s[k].tag=0;
return;
}
void build(int l,int r,int k) {
s[k].l=l,s[k].r=r,s[k].tag=0;
if(l==r) {
s[k].f[ans[l]]=l,s[k].f[ans[l]^1]=N;
s[k].e[ans[l]]=l,s[k].e[ans[l]^1]=-20;
return;
}
int mid=(l+r)/2;
build(l,mid,k*2),build(mid+1,r,k*2+1);
pushup(k);
return;
}
int check(int L,int R,int k,int x) {
int l=s[k].l,r=s[k].r;
if(l>=L&&r<=R) return s[k].f[x];
if(s[k].tag) pushdown(k);
int res=N;
int mid=(l+r)/2;
if(mid>=L) res=min(res,check(L,R,k*2,x));
if(mid<R) res=min(res,check(L,R,k*2+1,x));
return res;
}
int get(int L,int R,int k) {
int l=s[k].l,r=s[k].r;
if(l>=L&&r<=R) return s[k].e[1];
if(s[k].tag) pushdown(k);
int res=0;
int mid=(l+r)/2;
if(mid>=L) res=max(res,get(L,R,k*2));
if(mid<R) res=max(res,get(L,R,k*2+1));
return res;
}
void change(int L,int R,int k) {
int l=s[k].l,r=s[k].r;
if(l>=L&&r<=R) {swap(s[k].f[0],s[k].f[1]),swap(s[k].e[0],s[k].e[1]),s[k].tag^=1;return;}
if(s[k].tag) pushdown(k);
int mid=(l+r)/2;
if(mid>=L) change(L,R,k*2);
if(mid<R) change(L,R,k*2+1);
pushup(k);
return;
}
int main() {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
ans[a[i]]++,w=max(w,a[i]);
}
init();
build(1,N-25,1);
while(q--) {
int k,l;
scanf("%d%d",&k,&l);
if(a[k]!=l) {
int r=check(a[k],N-25,1,1);
change(a[k],r,1);
a[k]=l,r=check(l,N-25,1,0);
change(l,r,1);
w=get(1,N-25,1);
}
printf("%d\n",w);
}
return 0;
}