题意:
给长度为\(n\)的数列\(a\),每次可以删除两个相同的数\(x\),加入\(x+1\),此时\(n--\)。
问你能得到的最大的数是多少。
为了增加难度,给了你\(q\)次询问,每次修改一个数(对之后的询问都奏效),再问你同样的问题。
思路:
首先答案跟数列\(a\)的排列顺序无关,跟不同值的个数有关。
也容易想到有两个相同的数就马上把它们合并(这是它们唯一的作用,总不能留着过年)
设\(c_i\)表示值\(i\)在\(a\)中出现次数。
\(c_i\)个值\(i\)可以变成\(\left\lfloor\frac{c_i}{2}\right\rfloor\)个\(i+1\),\(c_{i+1}+\left\lfloor\dfrac{c_i}{2}\right\rfloor\)个可以变成……(总之这样下去)
得到\(i\)实际的个数为\(\left\lfloor\frac{\left\lfloor\frac{\left\lfloor\frac{c_1}{2}\right\rfloor+c_2}{2}\right\rfloor+c_3}{2}\right\rfloor....\)无限嵌套,这个下取整好烦啊,把柿子化简为递推式得到:
\(f_i= \left\lfloor\dfrac{f_{i-1}+c_i}{2}\right\rfloor\)
这个柿子会清爽很多,含义是:\(i\)对\(i+1\)贡献的量,值\(i\)的个数为\(f_{i-1}+c_i\)。
所以最后求的是最大的 \(i\) 满足\(f_{i-1}+c_i> 0\)
首先\(a\)中的最大值\(mx\)肯定是存在的,所以只用考虑大于\(mx\)出现的最大值,而且这部分的\(c_i=0\),即问题转化为\(f_{i-1}>0\)的最大\(i\)。
考虑维护\(f_i\),每次修改相当于一个\(c_i++\)另一个\(c_j--\)。
\(c_i++/--\)对\(f\)的贡献,取决于下取整分子的奇偶性。
因此维护\(g_i\)表示\(f_{i-1}+c_i\)为奇(1)/偶(0)。
显然\(c_i++\)只会在\(g_i\)为奇数的时候使\(f_i++\),\(f_i++\)又往\(f_{i+1}\)贡献。发现是找到从\(i\)开始到\(r\)满足\(g\)都为\(1\)的极大段**(\(g_{r+1}=0\)),然后把\(f_l....f_{r}\)区间+1,\(g_l...g_{r+1}\)区间反转,别忘了\(g_{r+1}\)也需要反转(我为这个调了好久)。
\(c_i--\)的增量以及奇偶性判断恰好是反过来的。
因此,我们需要:区间/单点修改(加),区间(单点)查询,找从\(l\)开始\(g\)为\(k\)的极长段,找最靠右的\(f_i>0\)的\(i\)
最后一个操作直接二分,倒数第二个操作先要找到划分(找到的顺序自然是从左往右的),然后对于一个划分如果全部满足就结束讨论下一个,否则再分治(二分)下去。因为只有一个划分(区间)会分治下去,再加上所有的操作总复杂度为\(O(nlogn)\)
然而我看到其它人很短的代码,就知道我的思路太逊了,不过能自己解出来题也很开心啦。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,q,a[N],c[N],F[N],ls[N],nd,rs[N];
priority_queue<int> Q;
int D[N]; //可删堆
void Q_del() {
while(!Q.empty()&&D[Q.top()]) {D[Q.top()]--;Q.pop();}
}
struct seg {
int l,r,len,tg,tf,g,f;
}T[N];
void P_dw(int x) {
if(T[x].tg!=-1) {
T[ls[x]].tg=T[rs[x]].tg=T[x].tg;
if(!T[x].tg) {T[ls[x]].g=T[rs[x]].g=0;}
else {T[ls[x]].g=T[ls[x]].len;T[rs[x]].g=T[rs[x]].len;}
T[x].tg=-1;
}
if(T[x].tf) {
T[ls[x]].tf+=T[x].tf;T[rs[x]].tf+=T[x].tf;
T[ls[x]].f+=T[x].tf*T[ls[x]].len;T[rs[x]].f+=T[x].tf*T[rs[x]].len;
T[x].tf=0;
}
}
void P_up(int x) {
T[x].g=T[ls[x]].g+T[rs[x]].g;
T[x].f=T[ls[x]].f+T[rs[x]].f;
}
int Query(int x,int p) {
if(T[x].l==T[x].r) {return T[x].g;}
P_dw(x);
int mid=(T[x].l+T[x].r)>>1;
return (p<=mid)?Query(ls[x],p):Query(rs[x],p);
}
void Build(int &x,int l,int r) {
x=++nd; T[x]=(seg){l,r,r-l+1,-1,0,0,0};
if(l==r) {T[x].f=F[l];T[x].g=((F[l-1]+c[l])&1);return;}
int mid=(l+r)>>1;
Build(ls[x],l,mid),Build(rs[x],mid+1,r);
P_up(x);
}
void Update(int x,int l,int r,int dg,int df) {
if(l<=T[x].l&&T[x].r<=r) {
T[x].f+=df*T[x].len;T[x].g=(!dg)?0:T[x].len;
T[x].tf+=df;T[x].tg=dg;
return;
}
P_dw(x);
int mid=(T[x].l+T[x].r)>>1;
if(l<=mid) Update(ls[x],l,r,dg,df);
if(r>mid) Update(rs[x],l,r,dg,df);
P_up(x);
}
void update(int x,int p) { //g[p]^1
if(T[x].l==T[x].r) {T[x].g^=1;return;}
P_dw(x);
int mid=(T[x].l+T[x].r)>>1;
(p<=mid)?update(ls[x],p):update(rs[x],p);
P_up(x);
}
//g整段的值是否为vf
bool check(int x,int vg) {return T[x].g==(!vg?0:T[x].len);}
int findG(int x,int L,int vg) { //the rightest G (左边能满足,右边才可以)
if(T[x].r<L)return -1;
if(T[x].l==T[x].r) {
if(!check(x,vg))return -1;
return T[x].l;
}
if(T[x].l>=L) { //一段划分
if(check(x,vg)) return T[x].r;
P_dw(x);
if(check(ls[x],vg)) {return max(T[ls[x]].r,findG(rs[x],L,vg));}
else return findG(ls[x],L,vg);
}
P_dw(x);
int mid=T[ls[x]].r;
if(mid<L) return findG(rs[x],L,vg);
int w=findG(ls[x],L,vg); //先找到 [L,n]的划分(从左到右)
return w==mid?max(w,findG(rs[x],L,vg)):w;
}
int findF(int x,int L) { //the rightest F(只要最右边有一个满足了即可)
if(T[x].r<L) {return -1;}
if(T[x].l==T[x].r) {
assert(T[x].f>=0);
if(!T[x].f) return -1;
return T[x].l;
}
P_dw(x);
assert(T[rs[x]].f>=0);
if(!T[rs[x]].f) return findF(ls[x],L);
return findF(rs[x],L);
}
void Del(int x) {
int w=Query(1,x);
// printf("Del x=%d c[x]=%d w=%d\n",x,c[x],w);
if(!(w&1)) {
int r=findG(1,x,0);
assert(r!=-1); //*
// printf("!r=%d\n",r);
Update(1,x,r,1,-1);
if(r<m)update(1,r+1);
}
else {update(1,x);}
c[x]--;
}
void Add(int x) {
int w=Query(1,x);
// printf("Add x=%d c[x]=%d w=%d\n",x,c[x],w);
if(w&1) {
int r=findG(1,x,1);
assert(r!=-1);
// printf("!r=%d\n",r);
Update(1,x,r,0,1);
if(r<m)update(1,r+1);
}
else {update(1,x);}
c[x]++;
}
int main() {
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){scanf("%d",&a[i]);c[a[i]]++;Q.push(a[i]);}
m=2e5+20; //+log次
for(int i=1;i<=m;i++) {F[i]=(F[i-1]+c[i])/2;}
int rt;Build(rt,1,m);
while(q--) {
int k,v;
scanf("%d%d",&k,&v);
D[a[k]]++;Q.push(v);Q_del();
Del(a[k]),Add(v);
a[k]=v;
int mx=Q.top();
// printf("mx=%d\n",mx);
int r=findF(1,mx);
// printf("r=%d\n",r);
printf("%d\n",(r==-1)?mx:r+1);
}
return 0;
}
//完结撒花