Portal --> loj2472

Solution

  感觉是一道很有意思的贪心题啊ovo(想了一万个假做法系列==)

  比较直观的想法是,既然一个数\(i\)只会对应一个\(\lfloor\frac{i}{k}\rfloor\),那么这个不小于关系可以建成一棵树的样子,每个节点的编号代表这个节点对应的位置,然后第\(i\)号点的父亲是\(\lfloor\frac{i}{k}\rfloor\)

  然后这个时候我们可以想到一个初步的贪心策略:注意到\(d\)的顺序并不影响结果,所以我们可以考虑将\(d\)从小到大排序之后,我们按照深度一层一层处理(假设这层的节点数量为\(x\)),先从\(d\)中没有分配的位置中取出前\(x\)个数,然后从大到小分配到这层的节点上

  但是这是一个假做法qwq只能在\(d\)互不相同的情况下保证正确性

  如果说存在相同的\(d\),那么可能会出现这种情况:

\[\begin{aligned}
d&=\{1,1,1,2\}\\
YourAns&=\{1,1,1,2\}\\
RealAns&=\{1,1,2,1\}
\end{aligned}
\]

  因为相同的值我们其实可以放到子树里面去,然后我们就可以用更大的值来填这层的点了

  这个时候注意到,我们可能需要“预留”一些值放到子树里面去,所以我们换一个角度来思考问题:我们将\(d\)从大到小排序,考虑如果我们将某一个位置\(i\)的值分配给当前节点\(x\),那么必须保证不小于\(d_i\)的数最少要有\(sz[x]\)个(其中\(sz[x]\)表示\(x\)的子树大小),因为我们已经将\(d\)排了序,所以其实相当于要保证\(i\)以及前面的还没有被分配掉的位置最少要有\(sz[x]\)个,如果说有多个相同的数,那么从贪心的角度来看肯定是要取最靠右的那个

  所以我们可以用线段树维护一个\(f\)数组,\(f[i]\)表示在\(i\)及以前的被分配掉的位置数量,那么我们按顺序给每个节点填数,每次只要找到一个\(d\)值最大的位置\(i\)满足\(i\)以前的每个位置\(j\)都满足\(j-f[j]>=sz\)即可(也就是最靠左的满足条件的那个值),然后如果说这个值有多个,把最靠右的位置分配给当前节点(记这个位置为\(x\)),并且下标为\(x\sim n\)的\(f\)值就应该对应地加上\(sz\)(因为要给当前节点的子树在前面预留位置),所以实现一个区间加和查询就好了

  这里还有一个小问题,每次我们都选择当前值最靠右的位置拿来分配,那如果说这个位置之前已经被选过一次了呢?这里其实可以理解为一个比较抽象的处理:我们可以理解为把原来在这个位置的那个点以及前面的点往前移一位(如果有空位那么前面就不用再移了),把最右边的那个位置空出来给当前点(不会出现这段值相同的位置已经全被选完了还会再选到这个值情况),所以处理的时候每次直接将最右边的那个分配出去即可

  最后还有一点就是,因为我们在处理每个节点的时候会为其子树预留位置,所以在处理到一个点的时候,应该把父亲预留的位置给去掉(但不能把父亲的位置也给去掉了,所以应该是减去\(sz[fa]-1\)的贡献)

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5*(1e5)+10,SEG=N*4;
namespace Seg{/*{{{*/
int ch[SEG][2],tag[SEG],mn[SEG];
int n,tot;
void pushup(int x){mn[x]=min(mn[ch[x][0]]+tag[ch[x][0]],mn[ch[x][1]]+tag[ch[x][1]]);}
void _build(int x,int l,int r){
tag[x]=0;
if (l==r){mn[x]=l;return;}
int mid=l+r>>1;
ch[x][0]=++tot; _build(ch[x][0],l,mid);
ch[x][1]=++tot; _build(ch[x][1],mid+1,r);
pushup(x);
}
void build(int _n){n=_n; tot=1; _build(1,1,n);}
void _update(int x,int l,int r,int lx,int rx,int delta){
if (l<=lx&&rx<=r) {tag[x]+=delta; return;}
int mid=lx+rx>>1;
if (r<=mid) _update(ch[x][0],l,r,lx,mid,delta);
else if (l>mid) _update(ch[x][1],l,r,mid+1,rx,delta);
else{
_update(ch[x][0],l,mid,lx,mid,delta);
_update(ch[x][1],mid+1,r,mid+1,rx,delta);
}
pushup(x);
}
void update(int l,int r,int delta){_update(1,l,r,1,n,delta);}
int _query(int x,int lx,int rx,int k,int tg){
tg+=tag[x];
if (lx==rx) return (mn[x]+tg)>=k?lx:lx+1;
int mid=lx+rx>>1,rval=mn[ch[x][1]]+tg+tag[ch[x][1]];
if (k<=rval) return _query(ch[x][0],lx,mid,k,tg);
else return _query(ch[x][1],mid+1,rx,k,tg);
}
int query(int k){return _query(1,1,n,k,0);}
}/*}}}*/
int d[N],ans[N],ed[N],fa[N],sz[N];
int clean[N];
int n;
double K;
bool cmp(int x,int y){return x>y;}
void prework(){
for (int i=n;i>=1;--i){
fa[i]=(int)(1.0*i/K);
ed[i]=i; ++sz[i];
sz[fa[i]]+=sz[i];
if (i!=n&&d[i]==d[i+1]) ed[i]=ed[i+1];
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d%lf",&n,&K);
for (int i=1;i<=n;++i) scanf("%d",d+i);
sort(d+1,d+1+n,cmp);
Seg::build(n);
prework();
for (int i=1;i<=n;++i){
if (fa[i]&&!clean[fa[i]]){
Seg::update(ans[fa[i]],n,sz[fa[i]]-1);
clean[fa[i]]=true;
}
ans[i]=Seg::query(sz[i]);
ans[i]=ed[ans[i]];
Seg::update(ans[i],n,-sz[i]);
}
for (int i=1;i<=n;++i) printf("%d ",d[ans[i]]);
printf("\n");
}

【loj2472】IIIDX的更多相关文章

  1. 【BZOJ5249】IIIDX(贪心,线段树)

    题意: 思路:赛季结束之前余总推荐的一道好题,不愧是余总 From https://www.cnblogs.com/suika/p/8748115.html 简略的说就是在预留足够多的位置的前提下贪心 ...

  2. Python高手之路【六】python基础之字符串格式化

    Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...

  3. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  4. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  6. 【调侃】IOC前世今生

    前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...

  7. Python高手之路【三】python基础之函数

    基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...

  8. Python高手之路【一】初识python

    Python简介 1:Python的创始人 Python (英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种解释型.面向对象.动态数据类型的高级程序设计语言,由荷兰人Guido ...

  9. 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】

    说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin ————————— ...

随机推荐

  1. 车牌,车架号,VIN码毫秒识别技术,汽车后市场的春天到来了

    vin码(车架号)识别运用 不仅在制造.销售.保养.保险.车辆评估.交易环节会需要录入汽车的VIN码,在交通事故处理中,作为汽车身份唯一识别码,VIN码是处理事故的执法人员必须要记录的信息之一.随着汽 ...

  2. 从源码角度彻底理解ReentrantLock(重入锁)

    目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 ...

  3. Parcel 打包器简单使用记录

    本文是构造 UI 轮子过程中搭建项目初始化时使用 Parcel 作为打包器的简要使用记录. 安装 参考 官方文档 使用 npm 进行 parcel-bundler 的安装. npm i -D parc ...

  4. k8s zookeeper、kafka部署

    安装zookeeper apiVersion: v1 kind: ConfigMap metadata: name: zookeeper-config namespace: kube-system a ...

  5. 228. [LeetCode] Summary Ranges

    Given a sorted integer array without duplicates, return the summary of its ranges. Example 1: Input: ...

  6. “Hello World!”团队第十四次会议

    今天是我们团队“Hello World!”团队召开的第十四次会议.博客内容: 一.会议时间 二.会议地点 三.会议成员 四.会议内容 五.Todo List 六.会议照片 七.燃尽图 一.会议时间 2 ...

  7. 20172305 2018-2019-1 《Java软件结构与数据结构》第六周学习总结

    20172305 2018-2019-1 <Java软件结构与数据结构>第六周学习总结 教材学习内容总结 本周内容主要为书第十章内容: 树(一种非线性结构,其中的元素被组织成一个层次结构) ...

  8. Android开发第二阶段(3)

    今天:对闹钟代码的按钮事件进行了添加和修改.对监听器的相关应用也有了进一步的了解和深入. 明天:对主界面的代码的优化比如对按钮位置的调节等细节处理.

  9. 浅谈Java变量的初始化顺序详解

    规则1(无继承情况下):对于静态变量.静态初始化块.变量.初始化块.构造器,它们的初始化顺序依次是(静态变量.静态初始化块)>(变量.初始化块)>构造器证明代码: 复制代码 代码如下: p ...

  10. 《我是IT小小鸟》阅读心得

    虽然读这本书是老师布置的作业,但是读了几页后就被书中的内容所吸引住了.或许是因为我也是学这个专业的,所以书中的一些内容让我觉得非常的有兴趣.作为一个学习软件工程的大一学生还没真正的认识到这个专业的深奥 ...