P11820 [PA 2015] 健身房 / Siłownia
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\) 个人:
- 如果目前健身房在 \([l_i,r_i]\) 都没有人,或者有人的时刻器材 \(p_i\) 已经被占用,那么这个人应该尽量在接近右端点的时刻,新开一天,去健身。因为这样后面的人更有机会和他在同一天健身。
- 如果健身房目前在 \([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)\) 的。提供一些卡空间方法:
- 结构体注意地址对齐什么的问题。
- 一个指针的空间在 64 位机子上与 long long 相同,所以不能用指针写动态开点线段树了/ll。
- 线段树节点不要存 \(l,r\)。
- 更新完所有 \(r_i\) 之后,只有所有的时刻 \(r_i\) 是有用的,有用时刻只有 \(n\) 个。
- 注意到该题时限 \(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();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/19111861

浙公网安备 33010602011771号