Codeforces 1737G - Ela Takes Dancing Class(平衡树)

数据结构好题。

先考虑如果 \(s_i\) 全是 \(1\) 怎么做。考虑一个非常特殊的状态:如果当前最靠左的舞蹈者跳一步就能跳到最靠右的舞蹈者的右边,那么这样的局面性质其实是非常完美的。因为容易归纳证明,这样的局面下,每一步最靠左的舞蹈者跳一步都能跳到最靠右的舞蹈者的右边,这样一来,如果维护出了初始局面下 \(n\) 个人坐标排序后的结果,那么就可以 \(O(1)\) 地算出 \(k\) 轮以后某个人的位置,进而求出答案。

考虑更一般的情况,如果第一步中第 \(1\) 个人恰好跳过了第 \(x\) 个人,那么我们就称当前前 \(x\) 个人形成了一个“完美局面”。那么这个过程显然是,随着时间线的推移,\(x\) 不断增加。由于 \(x\) 的变化是 \(O(n)\) 的,所以我们只要知道,如果当前前 \(x\) 个人形成了“完美局面”,至少需要多少轮后第一个人能够跳过第 \(x+1\) 个人即可。这一部分我们二分答案 \(mid\)。先假设前 \(mid\) 轮没有跳过第 \(x+1\) 个人,算一下 \(mid\) 轮后最后一个人的位置,如果超过第 \(x+1\) 个人当前的位置说明 \(mid\) 轮内 \(x\) 已经增加了,因为如果跳过了 \(x+1\) 那么 \(mid\) 轮后最后一个人的位置肯定比我们计算出的值大。使用平衡树维护即可。对于一个完美局面而言,进行 \(mid\) 轮后的结果对顺序的影响实际上是将一个前缀 cyclic shift。除此之外还需要实现区间加与查询第 \(k\) 小的值的位置。这是容易用平衡树维护的。

接下来考虑有 \(s_i=0\) 的情况,其实也比较好办,对于 \(s_i=0\) 的舞蹈者,先当他是可以移动的,然后计算下最早什么时候会出现“坐标最小的舞蹈者是不可移动”的情况,如果这段时间内 \(x\) 没有增加,就暴力跳到那一时刻然后将最小的元素从平衡树里删掉即可。

时间复杂度 \(n\log^2n\)

const int MAXN=1e5;
const int INF=0x3f3f3f3f;
const ll INFll=0x3f3f3f3f3f3f3f3fll;
int n,d,qu,A[MAXN+5],B[MAXN+5];ll st[MAXN+5],res[MAXN+5];int st_cnt;
struct qry{
	int t,k,id;
	friend bool operator <(const qry &X,const qry &Y){
		return X.t<Y.t;
	}
}q[MAXN+5];
mt19937 rng(time(0));
struct node{int ch[2],key,siz;ll pos,lz;int typ,has;}s[MAXN+5];
int ncnt,rt;
int nwnd(int x,int y){
	++ncnt;s[ncnt].siz=1;s[ncnt].key=rng();
	s[ncnt].pos=x;s[ncnt].typ=s[ncnt].has=!y;
	return ncnt;
}
void tag(int k,ll v){if(k)s[k].pos+=v,s[k].lz+=v;}
void pushdown(int k){
	if(s[k].lz){
		if(s[k].ch[0])tag(s[k].ch[0],s[k].lz);
		if(s[k].ch[1])tag(s[k].ch[1],s[k].lz);
		s[k].lz=0;
	}
}
void pushup(int k){
	s[k].has=s[s[k].ch[0]].has|s[s[k].ch[1]].has|s[k].typ;
	s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz+1;
}
void split(int k,int sz,int &a,int &b){
	if(!k)return a=b=0,void();pushdown(k);
	if(sz<=s[s[k].ch[0]].siz)b=k,split(s[k].ch[0],sz,a,s[k].ch[0]),pushup(k);
	else a=k,split(s[k].ch[1],sz-s[s[k].ch[0]].siz-1,s[k].ch[1],b),pushup(k);
}
int merge(int a,int b){
	if(!a||!b)return a+b;pushdown(a);pushdown(b);
	if(s[a].key<s[b].key)return s[a].ch[1]=merge(s[a].ch[1],b),pushup(a),a;
	else return s[b].ch[0]=merge(a,s[b].ch[0]),pushup(b),b;
}
int get_fst(int k){
	if(!s[k].has)return INF;if(!k)return 0;
	if(s[s[k].ch[0]].has)return get_fst(s[k].ch[0]);
	else if(s[k].typ)return s[s[k].ch[0]].siz+1;
	else return s[s[k].ch[0]].siz+1+get_fst(s[k].ch[1]);
}
ll get_kth(int x){
	int k1,k2,p;split(rt,x,k1,k2);p=k1;
	while(s[p].ch[1])pushdown(p),p=s[p].ch[1];
	ll res=s[p].pos;rt=merge(k1,k2);return res;
}
int get_cnt(){
	int L=1,R=s[rt].siz,p=0;ll v1=get_kth(1);
	while(L<=R){
		int mid=L+R>>1;
		if(get_kth(mid)<=v1+d+mid-2)p=mid,L=mid+1;
		else R=mid-1;
	}return p;
}
ll get_nxt(int x,int stp){
	int ps=(stp-1)%x+1;
	return get_kth(ps)+1ll*((stp-1)/x+1)*(d+x-1);
}
int main(){
	scanf("%d%d%d",&n,&d,&qu);
	for(int i=1;i<=n;i++)scanf("%d",&A[i]);
	for(int i=1;i<=n;i++)scanf("%1d",&B[i]);
	for(int i=1;i<=qu;i++)scanf("%d%d",&q[i].t,&q[i].k),q[i].id=i;
	for(int i=1;i<=n;i++)rt=merge(rt,nwnd(A[i],B[i]));
	sort(q+1,q+qu+1);int cur=1,tim=0;
	pii pre=mp(0,0);
	while(cur<=qu){
		int pos=get_fst(rt);
		if(pos==1){
			int k1,k2;split(rt,1,k1,k2);rt=k2;
			st[++st_cnt]=s[k1].pos;continue;
		}
		int x=get_cnt();ll pos_x=(x==s[rt].siz)?INFll:get_kth(x+1);
		int L=1,R=(pos<=x)?(pos-1):INF,p=0;
		while(L<=R){
			int mid=L+R>>1;
			if(get_nxt(x,mid)<pos_x)p=mid,L=mid+1;
			else R=mid-1;
		}
		while(cur<=qu){
			if(q[cur].t>tim+p)break;
			if(q[cur].k<=st_cnt)res[q[cur].id]=st[q[cur].k];
			else{
				int _k=q[cur].k-st_cnt;
				if(_k>x)res[q[cur].id]=get_kth(_k);
				else{
					int ps=(_k+q[cur].t-tim-1)%x+1;
					int cnt=(q[cur].t-tim)/x+(ps<=(q[cur].t-tim)%x);
					res[q[cur].id]=get_kth(ps)+1ll*cnt*(d+x-1);
				}
			}
			++cur;
		}
		int k1,k2,k3;split(rt,x,k1,k3);split(k1,p%x,k1,k2);
		tag(k1,1ll*(d+x-1)*(p/x+1));tag(k2,1ll*(d+x-1)*(p/x));
		rt=merge(merge(k2,k1),k3);
		tim+=p;
	}
	for(int i=1;i<=qu;i++)printf("%lld\n",res[i]);
	return 0;
}
posted @ 2023-06-06 17:27  tzc_wk  阅读(52)  评论(0)    收藏  举报