P11820 [PA 2015] 健身房 / Siłownia

也许是另一种贪心做法,但是似乎需要卡空间,所以输麻了。

题意

有 \(m\) 个器材,有 \(n\) 个人要健身。

第 \(i\) 个人要在 \([l_i,r_i]\) 的其中一天使用器材 \(p_i\)。

构造每个人健身的时刻,使得健身房有人的天数最少。

\(n \le 10^6, m \le 10^9\)。

思路

首先器材可以离散化,变成 \(m \le 10^6\)。

首先想想有没有什么贪心做法。

先假设有解。

按照 \(l\) 扫描或者按照 \(r\) 扫描,升序或者降序,都试一下能不能贪心。

发现可以按照 \(r\) 升序排序。对于第 \(i\) 个人:

  1. 如果目前健身房在 \([l_i,r_i]\) 都没有人,或者有人的时刻器材 \(p_i\) 已经被占用,那么这个人应该尽量在接近右端点的时刻,新开一天,去健身。因为这样后面的人更有机会和他在同一天健身。
  2. 如果健身房目前在 \([l_i,r_i]\) 的其中几天有人,那么这个人尽量在接近左端点的,且那一天 \(p_i\) 有空的时刻健身。因为这样可以留出更靠后的时刻给同样使用 \(p_i\) 的后人。

对于情况 \(1\),如果 \(r_i\) 天 \(p_i\) 已经被占用,那么就要考虑第 \(r_i-1\) 天。不过这个应该是可以处理的。

但是假如 \([l_i,r_i]\) 期间 \(p_i\) 都被占用了,我们需要让其中一天占用 \(p_i\) 的人换到 \(<l_i\) 的某一天健身。这样贪心就需要反悔,很烦。


这一段感觉有教育意义。

我们有一种方法可以保证情况 \(1\) 直接选择 \(r_i\) 合法。

现在的问题是,可能有一些人选择的时刻太后了,导致后人没有时刻可以选择。

比如我们有 \(3\) 个要用同样器材的人:\([1,3],[3,4],[3,4]\)。

这时第一个人就只能选择 \([1,2]\),不能选择 \(3\)。而且他在 \([1,2]\) 的任意一天都可以。

于是我们更新一下每个人的右端点。具体地:

按照 \(l\) 降序排序,对于第 \(i\) 个人,他目前能选择的最后一个有空的时刻是 \(x\),那么将他的 \(r_i'\) 设成 \(x\)。

这样可以保证,只要第 \(i\) 个人在 \(r_i'\) 及之前健身,他就不会祸害后人。

这里可以顺便判出是否有解。


更新 \(r_i\) 时,如何找到最后一个有空的时刻?

可以使用 set 维护,每个器材开一个 set,维护所有没有被占用的时刻(以区间形式维护)。所有 set 的区间个数之和是 \(O(n)\) 的。

你也可以对每个器材开一个动态开点线段树,维护这个器材被占用的时刻。然后线段树二分找到 \(\le r_i\) 的最大的未被占用时刻。


按照 \(r\) 升序排序时,怎么在找到健身房有人且器材 \(p\) 有空的 \([l,r]\) 内最早的时刻?

你仍然可以使用 set 维护健身房有人的时刻,和每个器材有空的时刻。

但是怎么找同时满足健身房有人,器材有空的时刻?

这个我真的想不到怎么用 set 这一类线性空间的东西做。大蛇看到能不能教教/kel

我们可以使用动态开点线段树,维护健身房有人的时刻,和每个器材有人的时刻。

然后线段树二分,具体地,当前线段树节点是 \([l,r]\),如果 \([l,mid]\) 满足健身房有人且器材有空的时刻数量(即健身房有人减去该器材有人的时刻数量)\(>0\),那么就递归进 \([l,mid]\)。


时间复杂度 \(O(n \log V)\)。

但是动态开点线段树的空间是 \(O(n \log V)\) 的。提供一些卡空间方法:

  1. 结构体注意地址对齐什么的问题。
  2. 一个指针的空间在 64 位机子上与 long long 相同,所以不能用指针写动态开点线段树了/ll。
  3. 线段树节点不要存 \(l,r\)。
  4. 更新完所有 \(r_i\) 之后,只有所有的时刻 \(r_i\) 是有用的,有用时刻只有 \(n\) 个。
  5. 注意到该题时限 \(8s\),你可以适当地用时间换空间。

code

这个贪心套路应该要学会。

这个题的数据好像还蛮强的。

最大点空间 510MB。

空间应该能再卡卡。虽然卡得意犹未尽,但是都过了为什么还要卡。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
constexpr int N=1e6+7,lim=1e9;
int n,m,lx;
struct piii {
int id,l,r,p;
}a[N];
int anss[N];
bool cmpl (piii a,piii b) { return a.l < b.l; }
bool cmpr (piii a,piii b) { return a.r < b.r; }
int ap[N],ar[N];
struct node {
int s;
int ls,rs;
void newnode(int _s=0) { s=_s, ls=rs=0; }
}tr[N*50];
int cnt;
int rt[N];
void pushup(int u) {
tr[u].s=tr[tr[u].ls].s + tr[tr[u].rs].s;
}
int insert(int u,int l,int r,int L,int R) {
if(r<L || !tr[u].s) return 0;
if(l==r) {
tr[u].s=0;
return l;
}
int mid=(l+r)>>1;
if(tr[u].ls==0) tr[u].ls = cnt++, tr[tr[u].ls].newnode(mid-l+1);
if(tr[u].rs==0) tr[u].rs = cnt++, tr[tr[u].rs].newnode(r-mid);
int s=0;
if(R<=mid) s=insert(tr[u].ls,l,mid,L,R);
else {
s=insert(tr[u].rs,mid+1,r,L,R);
if(!s) s=insert(tr[u].ls,l,mid,L,R);
}
pushup(u);
return s;
}
int insert2(int u1,int u2,int l,int r,int L,int R) {
if(l>R || !(tr[u1].s-(r-l+1-tr[u2].s))) return 0;
if(l==r) {
tr[u2].s=0;
return l;
}
int mid=(l+r)>>1;
if(tr[u1].ls==0) tr[u1].ls = cnt++, tr[tr[u1].ls].newnode(0);
if(tr[u1].rs==0) tr[u1].rs = cnt++, tr[tr[u1].rs].newnode(0);
if(tr[u2].ls==0) tr[u2].ls = cnt++, tr[tr[u2].ls].newnode(mid-l+1);
if(tr[u2].rs==0) tr[u2].rs = cnt++, tr[tr[u2].rs].newnode(r-mid);
int s=0;
if(L>mid) s=insert2(tr[u1].rs,tr[u2].rs,mid+1,r,L,R);
else {
s=insert2(tr[u1].ls,tr[u2].ls,l,mid,L,R);
if(!s) s=insert2(tr[u1].rs,tr[u2].rs,mid+1,r,L,R);
}
pushup(u2);
return s;
}
void change(int u1,int u2,int l,int r,int x) {
if(l==r) {
tr[u1].s=1; tr[u2].s=0;
return;
}
int mid=(l+r)>>1;
if(tr[u1].ls==0) tr[u1].ls = cnt++, tr[tr[u1].ls].newnode(0);
if(tr[u1].rs==0) tr[u1].rs = cnt++, tr[tr[u1].rs].newnode(0);
if(tr[u2].ls==0) tr[u2].ls = cnt++, tr[tr[u2].ls].newnode(mid-l+1);
if(tr[u2].rs==0) tr[u2].rs = cnt++, tr[tr[u2].rs].newnode(r-mid);
if(x<=mid) change(tr[u1].ls,tr[u2].ls,l,mid,x);
else change(tr[u1].rs,tr[u2].rs,mid+1,r,x);
pushup(u1); pushup(u2);
}
void main() {
sf("%d%d",&n,&m);
rep(i,1,n) {
int l,r,p;
sf("%d%d%d",&l,&r,&p);
a[i]={i,l,r,p};
ap[i]=p;
}
sort(ap+1,ap+n+1);
m = unique(ap+1,ap+n+1)-ap-1;
rep(i,1,n) a[i].p = lower_bound(ap+1,ap+m+1,a[i].p) - ap;
bool ans=1;
sort(a+1,a+n+1,cmpr);
int rx=lim+1,it=n+1;
ar[it]=rx;
lx=1;
per(i,n,1) {
if(a[i].r<rx) rx=a[i].r, it=i;
else --rx;
if(rx==0) lx=i+1, rx=-1;
ar[i]=rx;
while(ar[it]>a[i].r) --it;
a[i].r=it;
}
sort(a+1,a+n+1,cmpl);
it=lx;
rep(i,1,n) {
while(ar[it]<a[i].l) ++it;
a[i].l=it;
}
rep(i,1,m) rt[i]=cnt++, tr[rt[i]].newnode(n-lx+1);
per(i,n,1) {
int l=a[i].l, r=a[i].r, p=a[i].p;
a[i].r=insert(rt[p],lx,n,l,r);
if(!a[i].r) {
ans=0;
break;
}
}
if(!ans) {
puts("NIE");
exit(0);
}
sort(a+1,a+n+1,cmpr);
cnt=0;
rt[0]=cnt++, tr[rt[0]].newnode(0);
rep(i,1,m) rt[i]=cnt++, tr[rt[i]].newnode(n-lx+1);
rep(i,1,n) {
int l=a[i].l, r=a[i].r, p=a[i].p;
int pos2=insert2(rt[0],rt[p],lx,n,l,r);
if(!pos2) {
change(rt[0],rt[p],lx,n,r);
anss[a[i].id]=r;
} else anss[a[i].id]=pos2;
}
pf("%d\n",tr[rt[0]].s);
rep(i,1,n) pf("%d\n",ar[anss[i]]);
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}

更正解的做法(Wuyanru 题解的做法)

看了第一篇题解,怎么这么牛。

首先按照前面所说更新 \(r_i\)。

从小到大枚举时间 \(t\)。

每个器材维护一个 set。

若 \(l_i = t\),把 \(i\) 加到 \(p_i\) 的 set 里面。

若 \(r_i = t\),那么 \(i\) 在 \(r_i\) 时刻健身,然后在其他非空的器材的 set 里面选择 \(r\) 最小的人在这一天健身。

哇这个做法太牛了,我的卡常做法太蠢啦,我要写一发代码,以验证我有没有理解错。

时间复杂度 \(O(n \log n)\),空间复杂度 \(O(n)\)。还好写,不用卡常。

#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {
constexpr int N=1e6+7,lim=1e9;
constexpr ll m1=1e6+1;
int n,m,lx;
struct piii {
int l,r,p;
}a[N];
int al[N],ar[N];
int ans[N];
bool cmpl (int x,int y) { return a[x].l < a[y].l; }
bool cmpr (int x,int y) { return a[x].r < a[y].r; }
int ap[N];
bool fl;
struct pii {
int l,r;
bool operator < (const pii b) const { return l == b.l ? r < b.r : l < b.l; }
};
set<pii> tr[N];
struct pi {
int id;
bool operator < (const pi b) const { return a[id].r == a[b.id].r ? id < b.id : a[id].r < a[b.id].r; }
};
set<pi> tr2[N];
void main() {
sf("%d%d",&n,&m);
rep(i,1,n) {
int l,r,p;
sf("%d%d%d",&l,&r,&p);
a[i]={l,r,p};
ap[i]=p;
al[i]=ar[i]=i;
} sort(ap+1,ap+n+1);
m = unique(ap+1,ap+n+1)-ap-1;
rep(i,1,n) a[i].p = lower_bound(ap+1,ap+m+1,a[i].p) - ap; fl=1;
sort(al+1,al+n+1,cmpl);
rep(i,1,m) tr[i].insert({1,lim});
per(i,n,1) {
int id=al[i];
int l=a[id].l, r=a[id].r, p=a[id].p;
auto it = tr[p].upper_bound({r,lim});
if(it==tr[p].begin()) {
fl=0;
break;
}
--it;
if(it->r < l) {
fl=0;
break;
}
int x=min(r,it->r);
a[id].r=x;
pii tmp=*it;
tr[p].erase(it);
if(tmp.l<=x-1) tr[p].insert({tmp.l,x-1});
if(x+1<=tmp.r) tr[p].insert({x+1,tmp.r});
}
if(!fl) {
puts("NIE");
exit(0);
} sort(ar+1,ar+n+1,cmpr);
int it=1;
vector<int> vec;
rep(i,1,n) {
int id=ar[i];
if(ans[id]) continue;
++ans[0];
int r=a[id].r;
while(it<=n && a[al[it]].l <= r) {
int x=al[it];
tr2[a[x].p].insert({x});
if(tr2[a[x].p].size()==1) vec.push_back(a[x].p);
++it;
}
vector<int> tmp;
for(int x : vec) {
ans[tr2[x].begin()->id]=r;
tr2[x].erase(tr2[x].begin());
if(!tr2[x].empty()) tmp.push_back(x);
}
vec=tmp;
}
pf("%d\n",ans[0]);
rep(i,1,n) pf("%d\n",ans[i]);
}
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}

P11820 [PA 2015] 健身房 / Siłownia的更多相关文章

  1. Linux之一次性安装开发工具:yum groupinstall Development tools

    [spark@sparksinglenode ~]$ yum grouplist | moreLoaded plugins: fastestmirror, refresh-packagekit, se ...

  2. 使用Visual Studio 2015 Community 开发windows服务

    昨天研究在.NET下开发Windows服务程序,期间遇到一些小问题,这里仅将自己的开发过程和需要注意的地方写下和广大网友分享……  1.基础   Windows服务是指系统启动时能够自己运行的程序.W ...

  3. 2015暑假多校联合---Cake(深搜)

    题目链接:HDU 5355 http://acm.split.hdu.edu.cn/showproblem.php?pid=5355 Problem Description There are m s ...

  4. NOI 2015 荷马史诗【BZOJ 4198】k叉Huffman树

    抱歉因为NOIP集训,好长时间没再写题解了. NOI 2015也就只有这道题一看就能懂了-- 4198: [Noi2015]荷马史诗 Time Limit: 10 Sec  Memory Limit: ...

  5. 2015 CTSC & APIO滚粗记

    o诶人太弱..... 记一发滚粗记以便治疗我的健忘症= = //文章会不定时修改,添加一些内容什么的...因此最好看一下刷新一下(因为有可能你正在看= =我正在写... 5.2 早上9点坐上长达11小 ...

  6. [ZJOI 2015]幻想乡战略游戏

    Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来, ...

  7. 2015 Multi-University Training Contest 6 solutions BY ZJU(部分解题报告)

    官方解题报告:http://bestcoder.hdu.edu.cn/blog/2015-multi-university-training-contest-6-solutions-by-zju/ 表 ...

  8. 我的2015年ccf的解答

    只做了前三个题,在本地调试好了,不知为什么错了,好歹做了那么久,就记录一下了(注:这不是标准答案,只是我给出的解答) 这是第一题的代码: #include<stdio.h> #includ ...

  9. 2015第六届蓝桥杯C/C++ B组

    奖券数目:枚举 有些人很迷信数字,比如带“4”的数字,认为和“死”谐音,就觉得不吉利.虽然这些说法纯属无稽之谈,但有时还要迎合大众的需求.某抽奖活动的奖券号码是5位数(10000-99999),要求其 ...

  10. CVPR 2015 papers

    CVPR2015 Papers震撼来袭! CVPR 2015的文章可以下载了,如果链接无法下载,可以在Google上通过搜索paper名字下载(友情提示:可以使用filetype:pdf命令). Go ...

随机推荐

  1. linux 目录结构 比较老

    简介 计算机操作系统实验指导 linux版 linux arch 与体系结构相关的核心代码 drivers 设备驱动程序 include 编译核心所需的头文件 init 系统初始化代码 mm 独立于C ...

  2. 重载 & 重写 在java 中

    简介 他们都是实现多态的方式之一, 重载是实现编译时的多态,属于静态分配, 重写是实现动态分配. 简单来说: 重载在一个类中实现, 同名函数, 但是不一样的参数. 重写: 在类继承中实现. 子类继承了 ...

  3. lingo 练习3

    简介 练习 question 有一个护士工作站点,每天(周一至周日)所需最少职员数量20,16,13,16,19,14,和12,并要求每个职员一周连续工作5天,试求每周所需最少职员数,并给出安排. c ...

  4. 低代码开发平台,可零代码发布API

    RestCloud低代码开发平台可以快速的开发企业级前后端分离的业务系统以及基于微服务架构的业务系统.平台通过建立数据模型和业务模型能够无代码快速的发布API服务,同时也能基于数据模型快速生成Java ...

  5. C-Kermit AND C-Kermit for Android

    C-KERMIT 10.0 TUTORIAL https://www.kermitproject.org/ck10tutor.html#commands In the present age of g ...

  6. win10系统出现虚拟内存不足的问题

    最近有电脑基地的用户发现,用windows10系统的电脑物理内存已经16G,不过在使用程序时还是会弹出虚拟内存不足,将关闭**程序的问题,而系统也变的很卡,但是,出现虚拟内存不足如何设置呢?下面技术员 ...

  7. Unity使用mklink开多个端

    mklink为windows自带的为目录或者文件创建另外一份链接的功能 此功能可以用于Unity开多个端,但是不需要复制文件 一个端修改,另外的端也被自带修改,本质是用同一份文件 %1 mshta v ...

  8. Hello World背后藏着什么秘密?一行代码看懂Java的“跨平台”魔法

    Java虚拟机(Java Virtual Machine,JVM)是Java生态的基石,不仅承载着"一次编写,随处运行"的核心使命,还通过即时编译优化机制,弥合抽象层与性能间的差距 ...

  9. CAS 5.3.1系列之自定义JDBC认证策略(三)

    CAS 5.3.1系列之自定义JDBC认证策略(三) CAS官方文档是介绍基于配置实现jdbc认证的,可以参考我博客:CAS 5.3.1系列之支持JDBC认证登录(二),不过我们也可以通过自定义认证策 ...

  10. 2025牛客多校第六场 D.漂亮矩阵 K.最大gcd C.栈 L.最小括号串 个人题解

    L.最小括号串 数组操作 #贪心 题目 思路 感谢Leratiomyces大佬赛时的提示,否则估计还一直签不了到() 首先,贪心地构造出最优情况:数组左半部分全是(,右半部分全是),随后通过判断给定的 ...