bzoj 5249: [2018多省省队联测]IIIDX 线段树维护贪心

本文介绍了一种使用在线段树处理包含重复元素的题目方法。通过预先处理每个子树并给它们分配空间,实现对节点的有效管理和更新。具体包括离散化、区间更新和查询等操作。

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

题目:点击打开链接

题解:点击打开链接

关键:处理相同的数。给每个子树预留位置,再按点标号一次处理。

        每次贪心尽量选最大,在线段树上二分

注意:先离散化,且不用去重(重复的数不影响,区间减的时候都会减掉)

            1-n的顺序从小到大,而不是从大到小

#include<bits/stdc++.h>
using namespace std;
#define maxn 500020
#define ls(x) (x << 1)
#define rs(x) ((x << 1) ^ 1)
#define mid(l,r) ((l + r) >> 1)

struct node{
	int next,to;
}e[maxn * 2];
int head[maxn],cnt,vis[maxn],fa[maxn],id[maxn],sz[maxn];
int a[maxn],b[maxn],n,tot;
int num[maxn],add[maxn << 2],mn[maxn << 2];
double k;

inline void adde(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}
inline void update(int x){
	mn[x] = min(mn[ls(x)],mn[rs(x)]);
}
void build(int x,int l,int r){
	if ( l == r ){ mn[x] = num[l]; return; }
	build(ls(x),l,mid(l,r));
	build(rs(x),mid(l,r) + 1,r);
	update(x);
}

void init(){
	for (int i = n ; i >= 1 ; i--){
		int x = floor((double)i / k);
		sz[i]++;
		if ( x ) fa[i] = x , adde(x,i) , sz[x] += sz[i];
	}
	sort(b + 1,b + n + 1);
	for (int i = 1 ; i <= n ; i++) a[i] = lower_bound(b + 1,b + n + 1,a[i]) - b , num[a[i]]++;
	for (int i = n ; i >= 1 ; i--) num[i] += num[i + 1];
	build(1,1,n);
}
inline void Add(int x,int d){
	add[x] += d , mn[x] += d;
}
inline void pushdown(int x){
	if ( add[x] ){
		Add(ls(x),add[x]) , Add(rs(x),add[x]);
		add[x] = 0;
	}
}
void modify(int x,int l,int r,int ls,int rs,int d){
	if ( ls <= l && rs >= r ){ Add(x,d); return; }
	pushdown(x);
	register int mid = (l + r) >> 1;
	if ( ls <= mid ) modify(ls(x),l,mid,ls,rs,d);
	if ( rs > mid ) modify(rs(x),mid + 1,r,ls,rs,d);
	update(x);
}
int query(int x,int l,int r,int sz){
	if ( l == r ) return mn[x] >= sz ? l : l - 1;
	pushdown(x);
	if ( sz <= mn[ls(x)] ) return query(rs(x),mid(l,r) + 1,r,sz);
	return query(ls(x),l,mid(l,r),sz);
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d %lf",&n,&k);
	for (int i = 1 ; i <= n ; i++) scanf("%d",&a[i]) , b[i] = a[i];
	init();
	for (int i = 1 ; i <= n ; i++){
		if ( fa[i] ) modify(1,1,n,1,id[fa[i]],sz[i]);
		id[i] = query(1,1,n,sz[i]);
		modify(1,1,n,1,id[i],-sz[i]);
	}
	for (int i = 1 ; i <= n ; i++) printf("%d ",b[id[i]]);
	printf("\n");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值