房租 解题报告

房租 解题报告

题目描述

现在你有一个 \([1,n]\) 的区间,现在你将面对 \(m\) 个询问,每个询问都形如 \((l_i,r_i)\) ,你需要按照时间顺序依次处理这些询问。

所有询问都会提前给出。对于询问 \((l_i,r_i)\),它可以被接受当且仅当:

  • \(r_i-l_i+1 \ge x\)

  • 已经被接受的区间任意一个区间与 \([l_i,r_i]\) 无交。

现在对于满足 \(x \in [1,n]\) 的任意一个 \(x\),求上述过程后,被接受区间的并的长度。

数据规模:\(n\le 5 \times 10^4\)\(m \le 1 \times 10^5\)

分析

首先我们发现,可被接受的区间个数为(调和基数):

\[\sum_{x=1}^n{\frac{n}{i}} \approx n\ln n \]

那么我们只需要考虑怎么快速找出这些点就好了。我想不到,但出题人想到了分治。

对于区间 \([l,r]\) ,我们可以找到第一个满足条件的区间 \([x,y]\),然后分治区间 \([l,x-1]\)\([y+1,r]\)

什么?你问我怎么找第一个满足条件的区间。条件不就是:

  • \(l \le x\)

  • \(y \le r\)

  • \([x,y]\) 的时间最小

这不就二维偏序。树套树维护一下就行。

启示

  • 没有想法就分治
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;i++)
#define Down(i,s,t) for(int i=s;i>=t;i--)
//#define ls (i<<1)
//#define rs (i<<1|1)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
    register int x=0,f=1;
    char c=getchar();
    while(c<'0' || '9'<c) f=(c=='-')?-1:1,c=getchar();
    while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return x*f;
}
void write(int x){
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=5e4+1000,LG=17*17,M=N*LG;
int n,m,ans[N],rk[N<<1];
struct Line{int l,r,id;}a[N<<1];
bool cmp(Line x,Line y){return x.r-x.l>y.r-y.l;}
int rt[N<<2],ls[M],rs[M],val[M],tot;
void build(int &i,int l,int r){
    i=++tot;
    if(l==r) return;
    int mid=l+r>>1;
    build(ls[i],l,mid);
    build(rs[i],mid+1,r);
}
//内层修改 把x位置赋值k
void update0(int &i,int l,int r,int x,int k){
    if(!i) i=++tot,val[i]=inf;//一定要赋初值
    val[i]=min(val[i],k);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid)
        update0(ls[i],l,mid,x,k);
    else
        update0(rs[i],mid+1,r,x,k);
}
//内层询问 [x,y]的最小权值
int query0(int i,int l,int r,int x,int y){
    if(!i) return inf;
    if(x<=l && r<=y) return val[i];
    int mid=l+r>>1;
    if(x>mid) return query0(rs[i],mid+1,r,x,y);
    if(mid>=y) return query0(ls[i],l,mid,x,y);
    return min(query0(ls[i],l,mid,x,y),query0(rs[i],mid+1,r,x,y));
}
//外层修改 把x位置线段树上的y位置赋值k
void update(int i,int l,int r,int x,int y,int k){
    update0(rt[i],1,n,y,k);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid)
        update(ls[i],l,mid,x,y,k);
    else
        update(rs[i],mid+1,r,x,y,k);
}
//外层询问 询问外层[x,n]区间的线段树上[1,y]区间的最小值
int query(int i,int l,int r,int x,int y){
    if(!i) return inf;
    if(x<=l && r<=n)
        return query0(rt[i],1,n,1,y);
    int mid=l+r>>1;
    if(x>mid) return query(rs[i],mid+1,r,x,y);
    if(mid>=n) return query(ls[i],l,mid,x,y);
    return min(query(ls[i],l,mid,x,y),query(rs[i],mid+1,r,x,y));
}
int slove(int l,int r){
    if(l>r) return 0;
    int res_id=query(1,1,n,l,r);
    if(res_id==inf) return 0;
    res_id=rk[res_id];
    return slove(l,a[res_id].l-1)+slove(a[res_id].r+1,r)+a[res_id].r-a[res_id].l+1;
}
int main()
{
    freopen("rent.in","r",stdin);
    freopen("rent.out","w",stdout);
    n=read(),m=read();
    For(i,1,m) a[i].l=read(),a[i].r=read(),a[i].id=i;
    sort(a+1,a+m+1,cmp);
    For(i,1,m) rk[a[i].id]=i;
    int index=1;
    build(rt[0],1,n);
    Down(i,n,1){
        while(index<=m && a[index].r-a[index].l+1>=i) update(1,1,n,a[index].l,a[index].r,a[index].id),index++;
        ans[i]=slove(1,n);
    }
    For(i,1,n)
        printf("%d\n",ans[i]);
    //cout<<tot;
    return 0;
}
posted @ 2025-07-13 21:03  XiaoZi_qwq  阅读(6)  评论(0)    收藏  举报