[CF2227E] It All Went Sideways 题解

[CF2227E] It All Went Sideways 题解

题目传送门

题目大意

给你一个 \(n \times n\) 的网格,其中从左往右数第 \(i\) 列有 \(a_i\) 个方块(每个方块的占据的空间为 \(1\times1\)),他们全部受重力影响堆叠,即从下往上紧密排列。

现在重力方向由竖直向下改为水平向右,因此方块的堆叠方式会发生类似下图的变化:

像玩2048一样。

小明可以进行至多一次如下操作:

  • \(a_i\) 减去 1 ,注意此操作发生在重力突变前,即等效于删去第 \(i\) 列最上面的方块。

小明也可以选择不进行操作。

目标是使重力转换后位置发生变化的方块数量(即图中橙色方块数量)最大,被删除的方块不被记入。

你需要找到这个数量并输出。

数据范围 \(n \le2\times10^5\) , \(1\le a_i\le n\) .

分析

发现小明的操作只会影响某一行的答案,因此我们考虑逐行计算对答案的贡献。

对于某一行:

  • 若小明对对这一行进行了删除操作,那么删去的方块所在位置往左的方块一定会在重力突变的过程中改变位置,因为这个方块制造了一个需要左侧填补的空白。此时这一行的答案为从最左侧到目前位置这块区域的方块数量。
  • 若小明没有对这一行进行操作,则同理可知,这一行的答案是这一行从右往左首个空位置以左区域的方块数量。
  • 对比上述两个情况可知,如果我们删除的地方在首个空位置的左侧是无意义的,因为不做操作的答案一定不会更劣,同时可以节省一次操作。

现在我们考虑使用数据结构维护这个可供每一行查询方块数量的要求。

发现简单扫描线可做,甚至可以只用写单点修改的线段树或者树状数组就行。

总修改次数不会超过列的数量,查询操作每行最多两次,所以时间复杂度 \(O(nlog_{2}n)\) ,完全绰绰有余。

代码

由于涉及到线段树,所以有点长。

不过模板的部分让AI写会节省很多时间。

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
const int N=214514;
typedef long long ll;
int t[N<<2];
int head[N],nxt[N<<1],v[N<<1],tot;
int a[N],b[N];
int ans_pred[N],ans_init[N];
ll ans,sum;
int n;
void bud(int k,int l,int r){
	if(l==r){
		t[k]=1;
		return;
	}
	int mid=(l+r)>>1;
	bud(k*2,l,mid);
	bud(k*2+1,mid+1,r);
	t[k]=t[k*2]+t[k*2+1];
	return;
}
void chg(int k,int l,int r,int pls,int num){
	if(l==r){
		t[k]=num;
		return;
	}
	int mid=(l+r)>>1;
	if(pls<=mid)chg(k*2,l,mid,pls,num);
	else chg(k*2+1,mid+1,r,pls,num);
	t[k]=t[k*2]+t[k*2+1];
	return;
}
int qry(int k,int l,int r,int x,int y){
	if(x<=l&&r<=y){
		return t[k];
	}
	int mid=(l+r)>>1;
	int ans=0;
	if(x<=mid)ans+=qry(k*2,l,mid,x,y);
	if(y>mid)ans+=qry(k*2+1,mid+1,r,x,y);
	return ans;
}
//bool cmp(int x,int y){
//	return a[x]<a[y];
//}
void add(int fr,int to){
	nxt[++tot]=head[fr];
	v[tot]=to;
	head[fr]=tot;
	return;
}
void work(){
	ans=0,sum=0,tot=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		head[i]=0;
		ans_pred[i]=0;
		ans_init[i]=0;
	}
//	ans_init[1]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		add(a[i],i);
//		b[i]=i;
	}
//	sort(b+1,b+1+n,cmp);
	bud(1,1,n);
	int firstzero=0;
	for(int i=1;i<=n;i++){
		for(int j=head[i];j!=0;j=nxt[j]){
			int now=v[j];
			if(now!=1)ans_pred[i]=max(ans_pred[i],qry(1,1,n,1,now-1));
		}
		for(int j=head[i];j!=0;j=nxt[j]){
			chg(1,1,n,v[j],0);
			firstzero=max(firstzero,v[j]);
		}
		if(firstzero)ans_init[i+1]=qry(1,1,n,1,firstzero);
		sum+=ans_init[i];
	}
	ans=sum;
	for(int i=1;i<=n;i++){
		ans=max(ans,sum-ans_init[i]+ans_pred[i]);
	}
	printf("%lld\n",ans);
} 
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		work();
	}
	return 0;
}
posted @ 2026-05-16 13:22  Liyanx_ArtI  阅读(25)  评论(0)    收藏  举报