【CF1523G】Try Booking

题目

题目链接:https://codeforces.com/problemset/problem/1523/G
William 想在之后的 \(n\) 天内出租他的公寓。有 \(m\) 个请求待 William 依次处理,对于第 \(i\) 个请求 \([l_i,r_i]\) 表示一人想在 \([l_i,r_i]\) 天内租借公寓,而这个请求会被接受当且仅当 \(r_i-l_i+1\ge x\)\([l_i,r_i]\) 这些天内公寓都是空闲的。请对每个 \(x=1\cdots n\) 输出公寓出租的总天数。
\(n\le 5\times 10^4, m\le 10^5, 1\le l_i\le r_i\le n\)

思路

不难发现,对于每一个 \(x\),同意的请求数量的上界是 \(\lfloor\frac{n}{x}\rfloor\),所以对于 \(x=1\cdots n\),同意的请求数量总数是 \(O(n\log n)\) 的。
考虑一个很 naive 的做法,对于每一个 \(x\),我们依次枚举所有请求,如果这个请求可以接受,那么相当于剩余可以被使用的时间分成了两个区间,继续往后枚举解决。
也就是有一个可以使用的时间区间 \([L,R]\)(初始时为 \([1,n]\)),我们寻找编号最小的,\(r_i-l_i+1\geq x\),且 \(L\leq l_i,r_i\leq R\) 间的请求 \(i\),通过这个请求,递归 \([L,l_i),(r_i,R]\) 这两个区间继续。
如果我们倒序枚举 \(x\),相当于需要支持加入新的点 \((l,r)\),查询一个子矩阵的编号最小的点。可以用树状数组套线段树解决。
又由于总共通过的请求数量是 \(O(n\log n)\) 的,所以操作次数也是 \(O(n\log n)\) 的,时间复杂度为 \(O(n\log^3 n+m\log^2 n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=100010,LG=16,Inf=1e9;
int n,m,rt[N],ans[N],L[N],R[N],id[N];

bool cmp(int x,int y)
{
	return R[x]-L[x]>R[y]-L[y];
}

struct SegTree
{
	int tot,minn[N*LG*4],lc[N*LG*4],rc[N*LG*4];
	SegTree() { memset(minn,0x3f3f3f3f,sizeof(minn)); }
	
	int update(int x,int l,int r,int k,int v)
	{
		if (!x) x=++tot;
		minn[x]=min(minn[x],v);
		if (l==r) return x;
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
			else rc[x]=update(rc[x],mid+1,r,k,v);
		return x;
	}
	
	int query(int x,int l,int r,int ql,int qr)
	{
		if (!x) return Inf;
		if (ql<=l && qr>=r) return minn[x];
		int mid=(l+r)>>1,res=Inf;
		if (ql<=mid) res=min(res,query(lc[x],l,mid,ql,qr));
		if (qr>mid) res=min(res,query(rc[x],mid+1,r,ql,qr));
		return res;
	}
}seg;

struct BIT
{
	void add(int x,int p,int v)
	{
		for (int i=x;i<=n;i+=i&-i)
			rt[i]=seg.update(rt[i],1,n,p,v);
	}
	
	int query(int x,int p)
	{
		int ans=Inf;
		for (int i=x;i;i-=i&-i)
			ans=min(ans,seg.query(rt[i],1,n,p,n));
		return ans;
	}
}bit;

int solve(int l,int r)
{
	int id=bit.query(r,l);
	if (id>=Inf) return 0;
	return solve(l,L[id]-1)+solve(R[id]+1,r)+R[id]-L[id]+1;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&L[i],&R[i]);
		id[i]=i;
	}
	sort(id+1,id+1+m,cmp);
	for (int i=n,j=1;i>=1;i--)
	{
		for (;j<=m && R[id[j]]-L[id[j]]+1>=i;j++)
			bit.add(R[id[j]],L[id[j]],id[j]);
		ans[i]=solve(1,n);
	}
	for (int i=1;i<=n;i++) cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2021-06-17 15:33  stoorz  阅读(53)  评论(0编辑  收藏  举报