BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)

题意

题目链接

Sol

不难发现题目给出的是一个树,其中\(\frac{i}{K}\)是\(i\)的父亲节点

首先,当\(d_i\)互不相同时,一个显然的贪心策略就是优先给编号小的分配较大的权值。可以排序后dfs完成。

但是,当\(d_i\)相同时,可能存在这样一种情况:把编号小的子树内权值较大的节点,和某个编号较大的根交换后,仍然满足要求

比如\(N = 4, K = 2, a = {1, 1, 1, 2}\)

此时直接贪心的话会输出\(1, 1, 1, 2\),实际上最优解为\(1, 1, 2, 1\)

这时候怎么办呢?

考虑一个节点\(p\)可以选择权值为\(x\)的条件是什么,因为该节点子树内的权值一定都比\(x\)大

因此对于每个权值小于\(x\)的数,权值比它大且可以选择的数至少为\(siz[p]\)个

同时根据贪心的策略,先遍历到的节点应该选大的权值。

这样就不难得到一个算法:

首先按权值从大到小排序,同时用线段树维护出每个位置权值比它大且能选择的位置个数

对于每个点\(p\),在线段树上二分出最大的满足条件的位置\(x\)。同时,当权值相同时,该位置应该更靠右。

然后再在区间\([x, n]\)中的所有点减去\(siz[x]\)

注意一个细节,当遍历到某个节点时,应该消去父亲对他的影响

写完代码 -> 过样例 -> 1A hhhhhh(虽然是抄的)

60分

#include<bits/stdc++.h>
#define sz(x) (int)x.size()
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N; double K;
int a[MAXN], ans[MAXN], res;
vector<int> v[MAXN];
int AddEdge(int x, int y) {
    v[x].push_back(y);
}
void dfs(int x) {
    for(int i = 0; i < sz(v[x]); i++) dfs(v[x][i]);
    if(x) ans[x] = a[res--];
}
int main() {
    scanf("%d%lf", &N, &K); res = N;
    //printf("%lf\n", K);
    for(int i = 1; i <= N; i++) {
        int pre = (int) floor(i / K);
        a[i] = read(); AddEdge(pre, i);
    }
    sort(a + 1, a + N + 1);
    dfs(0);
    for(int i = 1; i <= N; i++) printf("%d ", ans[i]);
    return 0;
}

AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int N; double K;
int cnt[MAXN], fa[MAXN], siz[MAXN], ans[MAXN], a[MAXN];
#define ls k << 1
#define rs k << 1 | 1
struct Node {
    int l, r, f, mn;
}T[MAXN << 2];
void update(int k) {
    T[k].mn = min(T[ls].mn, T[rs].mn);
}
void add(int k, int val) {
    T[k].f += val; T[k].mn += val; 
}
void pushdown(int k) {
    if(!T[k].f) return ;
    add(ls, T[k].f); add(rs, T[k].f); 
    T[k].f = 0;
}
void Build(int k, int ll, int rr) {
    T[k] = (Node) {ll, rr, 0, 0};
    if(ll == rr) {T[k].mn = ll; return ;}
    int mid = ll + rr >> 1;
    Build(ls, ll, mid); Build(rs, mid + 1, rr); 
    update(k);
}
void IntervalAdd(int k, int ll, int rr, int val) {
    if(ll <= T[k].l && T[k].r <= rr) {add(k, val); return ;}
    pushdown(k);
    int mid = T[k].l + T[k].r >> 1;
    if(ll <= mid) IntervalAdd(ls, ll, rr, val); 
    if(rr  > mid) IntervalAdd(rs, ll, rr, val);
    update(k);
}
int Query(int k, int sz) {
    if(T[k].l == T[k].r) return T[k].mn >= sz ? T[k].l : T[k].l + 1;
    pushdown(k);
    if(T[rs].mn >= sz) return Query(ls, sz);
    else return Query(rs, sz);
}
int main() {
    N = read(); cin >> K;
    for(int i = 1; i <= N; i++) {
        a[i] = read();
        int t = (int) floor(i / K); fa[i] = t; siz[i] = 1;
    }
    for(int i = N; i >= 0; i--) siz[fa[i]] += siz[i];
    Build(1, 1, N); 
    sort(a + 1, a + N + 1, greater<int>());
    for(int i = N - 1; i >= 1; i--) cnt[i] = (a[i] == a[i + 1] ? cnt[i + 1] + 1 : 0);
    for(int i = 1; i <= N; i++) {
        if(fa[i] && fa[i] != fa[i - 1]) IntervalAdd(1, ans[fa[i]], N, siz[fa[i]] - 1);
        int t = Query(1, siz[i]); t += cnt[t]; cnt[t]++; t -= (cnt[t] - 1); 
        ans[i] = t;
        IntervalAdd(1, t, N, -siz[i]);
    }
    for(int i = 1; i <= N; i++) printf("%d ", a[ans[i]]);
    return 0;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

51 Nod 1007 正整数分组【类01背包】

1007 正整数分组 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 将一堆正整数分为2组,要求2组的和相差最小。 例如:1...

2847
来自专栏人工智能LeadAI

Python 设计模式初探

本文章是在阅读精通Python设计模式(中文版)(https://book.douban.com/subject/26829015/),以及阅读 Mask R-...

3576
来自专栏Script Boy (CN-SIMO)

Java中随机数的产生方式与原理

查阅随机数相关资料,特做整理 首先说一下java中产生随机数的几种方式 在j2se中我们可以使用Math.random()方法来产生一个随机数,这个产生的随机数...

2630
来自专栏应兆康的专栏

100个Numpy练习【4】

翻译:YingJoy 网址: https://www.yingjoy.cn/ 来源: https://github.com/rougier/numpy-100...

4658
来自专栏数据结构与算法

1080 线段树练习 单点修改及区间查询

1080 线段树练习 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 一...

2804
来自专栏计算机视觉与深度学习基础

Leetcode 190 Reverse Bits

Reverse bits of a given 32 bits unsigned integer. For example, given input 432...

21210
来自专栏章鱼的慢慢技术路

Direct3D 11 Tutorial 7:Texture Mapping and Constant Buffers_Direct3D 11 教程7:纹理映射和常量缓冲区

在上一个教程中,我们为项目引入了照明。 现在我们将通过向我们的立方体添加纹理来构建它。 此外,我们将介绍常量缓冲区的概念,并解释如何使用缓冲区通过最小化带宽使用...

944
来自专栏linux、Python学习

数据可视化:Matplotlib的坐标轴管理

有兴趣的可以跟踪pyplot模块的figure函数,可以完整看见Figure的创建过程,由FigureManager创建与管理的。

2790
来自专栏数据结构与算法

07:清泉-改(prime+堆)

时间限制: 10000ms单个测试点时间限制: 1000ms内存限制: 512000kB描述 华北电力大学可以抽象为一张有n个点m条边的无向图. 现在所有的边都...

28110
来自专栏极客猴

Django 学习笔记之模型高级用法(下)

除了抽象模型,在模型中定义的字段都会成为表中的列。如果我们需要给模型指定其他一些信息,例如排序方式、数据库表名等,就需要用到 Meta。Meta 是一个可选的类...

912

扫码关注云+社区