线段树+贪心 【SWTR-01】Sunny's Crystals (洛谷 P5584)

本文详细解析了SWTR-01 Sunny’sCrystals算法问题,介绍了如何通过预处理d[]数组来确定元素w的位置,并使用线段树维护最小值,实现高效删除操作。代码示例展示了如何构建线段树、更新和查询,以找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【SWTR-01】Sunny’s Crystals

题目大意:

给你一个序列,删除所有价值为 w 的元素;

删除一个元素时必须保证该元素的位置为 2 的次幂;

每删除一个元素,该元素位置后面的元素往前移一个位置;

求最少删除次数;


基本贪心思路为:先删除位置 i 最大(并且为2的次幂)的元素 w ,删完了以后,发现没有符合条件的元素 w ,这时只能删除前面非 w 元素,从位置1开始删,就一定符合位置为2的次幂这个条件(因为1和2都是2的次幂);

可以预处理出d[],表示元素 w 的位置与上一个2的次幂的值的最小值;(也就是要删除元素的数量)

然后根据这个d[]进行建树,用线段树维护最小值,保证每次先删d[]最小的那个w元素;

每次删完,区间修改删除1—cnt所有元素的值,减去d[];

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=2000100;
const int M=50100;
const LL mod=10007;
int n,w,d[N],pos[N],ff[N];
vector<int>ve;
struct Node{
	int l,r,mi,lz;
}tr[N*4];
void pp(int k){tr[k].mi=min(tr[ls].mi,tr[rs].mi);}
void pd(int k){
	if(tr[k].lz){
		tr[ls].lz+=tr[k].lz,tr[rs].lz+=tr[k].lz;
		tr[ls].mi+=tr[k].lz,tr[rs].mi+=tr[k].lz;
	}
	tr[k].lz=0;
}
void build(int l,int r,int k){
	tr[k].l=l,tr[k].r=r,tr[k].lz=0;
	if(l==r){
		tr[k].mi=d[l];
		return;
	}
	int d=(l+r)>>1;
	build(l,d,ls);
	build(d+1,r,rs);
	pp(k);
}
void update(int l,int r,int w,int k){
	if(l>r) return;
	if(tr[k].l>=l&&tr[k].r<=r){
		tr[k].mi+=w;
		tr[k].lz+=w;
		return;
	}
	pd(k);
	int d=(tr[k].l+tr[k].r)>>1;
	if(l<=d) update(l,r,w,ls);
	if(r>d) update(l,r,w,rs);
	pp(k);
}
int query(int k,int w){
	if(tr[k].l==tr[k].r) return tr[k].l;
	pd(k);
	if(tr[rs].mi==w) return query(rs,w);
	else if(tr[ls].mi==w) return query(ls,w);
}
int main(){
	scanf("%d%d",&n,&w);
	int cnt=0,tot=0,l=0,po=1;
	for(int i=1;i<=n;i++){
		if((po<<1)==i) po<<=1;
		int x;scanf("%d",&x);
		if(x==w) d[++cnt]=i-po,pos[cnt]=i;
		else ff[++tot]=i;
	}
	build(1,cnt,1);
	for(int i=1;i<=cnt;i++){
		int mi=tr[1].mi,pp=query(1,mi);
		if(mi==0){
			ve.push_back(pos[pp]);
			update(pp,pp,1e9,1);update(pp+1,cnt,-1,1);
		}
		else{
			for(int j=1;j<=mi;j++) ve.push_back(ff[++l]);
			ve.push_back(pos[pp]);
			update(pp,pp,1e9,1);
			update(1,cnt,-mi,1);update(pp+1,cnt,-1,1);//1--cnt特别重要,然后才是pp+1到cnt 
		}
	}
	printf("%d\n",ve.size());
	for(int i=0;i<ve.size();i++) printf("%d ",ve[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值