题意:

给长度为\(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;
}

//完结撒花