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

· · 题解

小清新贪心。

题目链接。

题意

现在有 n 个人和 k 个健身器材。

i 个人需要在 [l_i,r_i] 中的任意一个时刻去健身房 p_i,一个健身房同一时刻只能有最多一个人。

一个方案的代价是,有多少时刻,满足这个时刻中健身房里有人。

构造一个代价最小的方案。

## 题解 首先来考虑,假如不存在两个人 $i,j$,使得 $r_i=r_j$ 且 $p_i=p_j$,答案怎么算。 可以发现这是一个贪心,因为每一个人进入健身房的时间应当是越晚越好,这样能让更多的人一起被安排。 具体过程是这样的,我们从小到大枚举时间 $t$。 首先若存在 $l_i=t$,这说明我们可以安排 $i$ 这个人了,将 $i$ 这个人加到 $p_i$ 这个 set 里面。 若存在一个人 $i$,满足 $r_i=t$,并且 $i$ 这个人还没有安排健身房。 那么我们知道,$t$ 这个时刻就一定得有人在健身房了,我们考虑安排哪些人去健身房。 显然,对于同一类人(如果 $p$ 相同我们就认为是一类人),应该选取一个 $r_i$ 最小的人去健身房,直接 set 里面找就好了。 显然,关键的 $t$ 只有 $O(n)$ 个(即每一个人的左右端点),那么这是一个 $O(n\log n)$ 的做法。 --- 考虑为啥不能用这个做法做原题。 因为如果存在两个人 $i,j$,有 $r_i=r_j$,并且 $p_i=p_j$。 那么我们在扫到 $r_i-1$ 这个时刻的时候,就必须要安排一个人上去,否则 $r_i$ 再安排的时候安排不下就爆炸了。 这个时候有一个方法是拿线段树或者什么东西维护一下,可能能做,但我们有更牛的做法。 假设此时存在这样的一对 $i,j$,我们不妨假设 $l_i\le l_j$。 那么我们直接令 $r_i\gets r_i-1$ 就好了。 为什么呢,考虑我们的限制说明了有 $[l_j,r_j]\subseteq [l_i,r_i]$,也就是说 $j$ 能去健身房的时刻 $i$ 也能去。 那么如果 $i$ 在 $r_i$ 这个时刻去了健身房,我们直接让 $i,j$ 两个人去健身房的时刻交换一下,显然交换完的方案依然合法。 所以如果有解,我们就一定能够构造出,$i$ 不在 $r_i$ 时刻去健身房的方案。 那么我们一直做这个操作,做到不存在这样的 $i,j$ 为止。 假设此时存在一个 $k$ 满足 $l_k>r_k$,那么无解。 否则我们可以跑上面那个 set 维护的贪心。 那么如何快速做这个操作呢,把所有 $p_i$ 相同的人放在一起,然后按照 $l_i$ 从大到小进行排序,然后 set 维护连续段即可。 时间复杂度 $O(n\log n)$,并且我们只使用了 set,这实在是太牛了! ## 题解 ```c++ #include<bits/stdc++.h> #define inf 0x3f3f3f3f3f3f3f3fll #define debug(x) cerr<<#x<<"="<<x<<endl using namespace std; using ll=long long; using ld=long double; using pli=pair<ll,int>; using pi=pair<int,int>; template<typename A> using vc=vector<A>; template<typename A,const int N> using aya=array<A,N>; inline int read() { int s=0,w=1;char ch; while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1; while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } inline ll lread() { ll s=0,w=1;char ch; while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1; while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } struct node { int l,r,p,id; }a[1000005]; struct nod { int id,op,t; nod(int id=0,int op=0,int t=0):id(id),op(op),t(t){} }; set<pi>rest[1000005]; set<int>S; int qans[1000005]; map<int,int>to; vc<nod>event; int n,k,c; set<pi>wtf; inline void cover(int tim) { vc<int>P; for(int i:S) { qans[a[(*rest[i].begin()).second].id]=tim; rest[i].erase(rest[i].begin()); if(!rest[i].size()) P.push_back(i); } for(int i:P) S.erase(i); } inline int get(int v) { int ans; auto it=wtf.upper_bound(pi(v+1,-1)); if(it==wtf.begin()||(--it)->second<v) ans=v,wtf.insert(pi(v,v)); else { int L=it->first,R=it->second;wtf.erase(it); while(true) { it=wtf.upper_bound(pi(v,-1)); if(it==wtf.begin()||(--it)->second<L-1){ ans=L=L-1;break;} L=it->first;wtf.erase(it); } wtf.insert(pi(L,R)); } return ans; } int main() { n=read(),k=read(); for(int i=1;i<=n;i++) a[i].l=read(),a[i].r=read(),a[i].p=read(),a[i].id=i; sort(a+1,a+n+1,[](node a,node b) { if(a.p!=b.p) return a.p<b.p; return a.l>b.l; }); for(int i=1;i<=n;i++) { if(!to.count(a[i].p)) to[a[i].p]=++c; a[i].p=to[a[i].p]; } for(int l=1,r;l<=n;l=r) { r=l+1; while(r<=n&&a[r].p==a[l].p) r++; // printf("%d ~ %d\n",l,r-1); for(int j=l;j<r;j++) { a[j].r=get(a[j].r); if(a[j].l>a[j].r) { printf("NIE\n"); return 0; } event.push_back(nod(j,0,a[j].l)); event.push_back(nod(j,1,a[j].r)); } wtf.clear(); } sort(event.begin(),event.end(),[](nod a,nod b) { if(a.t!=b.t) return a.t<b.t; return a.op<b.op; }); // for(int i=1;i<=n;i++) printf("%d %d %d\n",a[i].l,a[i].r,a[i].p); int ans=0; for(auto p:event) { if(p.op==0) { int q=p.id; rest[a[q].p].insert(pi(a[q].r,q)); S.insert(a[q].p); } else { int q=p.id,P=a[q].p; if(rest[P].find(pi(a[q].r,q))!=rest[P].end()) ans++,cover(a[q].r); } } printf("%d\n",ans); for(int i=1;i<=n;i++) printf("%d\n",qans[i]); return 0; } ```