「九省联考 2018」IIIDX

3 篇文章 0 订阅
1 篇文章 0 订阅
题面

在这里插入图片描述
在这里插入图片描述

分析

60 p t s 60pts 60pts 贪心做法很显然对吧,把它看成一棵树很显然对吧。

不妨将权值从小到大排序。

对于一个点 x x x 的子树,每次取 n − s i z [ x ] + 1 → n n-siz[x]+1\to n nsiz[x]+1n 的权值即可,这种贪心在权值不相等的情况是可行的。p.s. s i z [ x ] siz[x] siz[x] x x x 的子树大小。

而权值不相等时,这种做法显然有问题。不妨思考一下,对于一个点 x x x,要取第 i i i 小的权值要满足什么条件?对的,要满足 i i i 以及 i i i 右边(即比 a [ i ] a[i] a[i] 大)的权值还没取的个数大于等于 s i z [ x ] siz[x] siz[x]

p r e [ i ] pre[i] pre[i] i i i 以及 i i i 右边的权值还没取的个数。则 p r e [ i ] > = s i z [ x ] pre[i]>=siz[x] pre[i]>=siz[x],而每次我们要找到一个最大的 i i i,当然是用二分。

具体做法

p r e pre pre 数组用线段树维护。

更新 p r e pre pre 时,先找到最大的 i i i 记为 i m a x imax imax,由于我们不知道 i m a x imax imax 之后的到底该怎么取,就将 p r e [ 1 ] − p r e [ i m a x ] pre[1]-pre[imax] pre[1]pre[imax] 之间的权值全部减去 s i z [ x ] siz[x] siz[x] 即可。 (为了给子树的其他节点留位置)

i m a x imax imax 时,在线段树上二分,找到最右边的满足 p r e [ 1 ] → p r e [ i m a x ] pre[1]\to pre[imax] pre[1]pre[imax] 都大于等于 s i z [ x ] siz[x] siz[x],但若多个数字相同,要选最左边的。(贪心)

还有要注意的一点,我们寻找 x x x 的答案时,要把 f a [ x ] fa[x] fa[x] 给子树留的位置全部删除。(p.s.这里删一次即可,因为 f a [ x ] fa[x] fa[x] 是单调递增的)

Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#include <vector>
using namespace std;
const int MAXN = 5e5 + 5;
struct Segment_Tree {
	int L, R, Min, add;
}tree[MAXN << 2];
int n, a[MAXN], cnt[MAXN], ans[MAXN], fa[MAXN], Siz[MAXN], mp[MAXN];
double k;
vector <int> v[MAXN];
int dfs(int x) {
	Siz[x] = 1;
	for(unsigned int i = 0; i < v[x].size(); i ++) Siz[x] += dfs(v[x][i]);
	return Siz[x];
}
int Min(int x, int y) { return x < y ? x : y; }
void Build(int p, int l, int r) {
	tree[p].L = l; tree[p].R = r;
	if(l == r) { tree[p].Min = n - l + 1; return; }
	int mid = (l + r) >> 1;
	Build(p << 1, l, mid); Build(p << 1 | 1, mid + 1, r); tree[p].Min = Min(tree[p << 1].Min, tree[p << 1 | 1].Min); 
}
void Speard(int p) {
	if(tree[p].add) {
		tree[p << 1].Min += tree[p].add; tree[p << 1 | 1].Min += tree[p].add;
		tree[p << 1].add += tree[p].add; tree[p << 1 | 1].add += tree[p].add;
		tree[p].add = 0;
	}
}
void Change(int p, int l, int r, int v) {
	if(tree[p].L >= l && tree[p].R <= r) { tree[p].Min += v; tree[p].add += v; return; }
	Speard(p);
	int mid = (tree[p].L + tree[p].R) >> 1;
	if(mid >= l) Change(p << 1, l, r, v);
	if(mid < r) Change(p << 1 | 1, l, r, v);
	tree[p].Min = Min(tree[p << 1].Min, tree[p << 1 | 1].Min);
}
int ask(int p, int v) {
//	printf("|%d %d %d %d|\n", tree[p].L, tree[p].R, tree[p].Min, v);
	if(tree[p].L == tree[p].R) return (tree[p].Min >= v ? tree[p].L : tree[p].L - 1);
	Speard(p);
	if(tree[p << 1].Min >= v) return ask(p << 1 | 1, v);
	return ask(p << 1, v);
}
int main() {
	scanf("%d%lf", &n, &k);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]), mp[i] = i;
	sort(a + 1, a + 1 + n); a[0] = 0x3f3f3f3f;
	for(int i = 1; i <= n; i ++) a[i] == a[i - 1] ? cnt[i] = cnt[i - 1] : cnt[i] = i; // 找这个数值最左边的位置 
	for(int i = 1; i <= n; i ++) v[(int)floor(i / k)].push_back(i), fa[i] = (int)floor(i / k);
	for(int i = 1; i <= n; i ++) if(!Siz[i]) Siz[i] = dfs(i);
	Build(1, 1, n); 
	for(int i = 1; i <= n; i ++) {
		if(fa[i] != fa[i - 1] && fa[i]) Change(1, 1, ans[fa[i]], Siz[fa[i]] - 1);
		int t = ask(1, Siz[i]); ans[i] = mp[cnt[t]]; mp[cnt[t]] ++;
		Change(1, 1, ans[i], -Siz[i]);
	}
	for(int i = 1; i <= n; i ++) printf("%d ", a[ans[i]]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值