[CF1523G]Try Booking

[CF1523G]Try Booking

壹、题目描述 ¶

传送门 to CF.

贰、题解 ¶

简直就是妙妙题......

有一个可以想到的是 —— 会不会相邻 \(x\) 的答案有通性?但是立马就被否决了,假设 \(x\) 变小的时候,相应的就会有一些区间合法,那么就会有一些区间因为前面的区间合法而变得不合法,然后又因为前面的不合法,后面又有一些区间合法......不难发现,\(x\) 的变化,带来的影响无穷尽也。

但对于一个 \(x\) 考虑的是长度大于等于 \(x\) 的区间,不难发现 \(x\) 的区间一定是 \(x+1\) 的区间的超集。但是这个东西有什么用呢?

考察一个 \(x\) 的区间集合中合法区间的数量,显然不超过 \(\lfloor{n\over x}\rfloor\),那么,对于每个 \(x\) 都找到合法区间的时间复杂度为:

\[{n\over 1}+{n\over 2}+{n\over 3}+\cdots+{n\over n}=n\ln n \]

也就是说,只要我们不去找那些其他的区间,知道每次询问我们单刀直入,找到我们想要的区间,那么最后的复杂度只会是 \(\mathcal O(n\ln n\times K)\)(其中 \(K\) 为找到合法区间所用的时间)的复杂度!

考虑递归,对于区间 \([L,R]\),我们找到满足 \(L\le l\le r\le R\) 的区间中编号最小的区间,然后再递归 \([L,l-1],[r+1,R]\) 进行处理,而找到那个区间是一个动态二维偏序,考虑使用树套树。

所以最后复杂度为 \(\mathcal O(n\ln n\log^2n)\). 代码实现的时候可以将长度倒着来,这样就没有必要在线段树中删区间,而是可以无脑加区间了。

叁、参考代码 ¶

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;

#define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

const int maxn=5e4;
const int maxm=1e5;
const int inf=0x3f3f3f3f;

struct interval{
    int l, r, id;
    interval(){}
    interval(int L, int R, int I): l(L), r(R), id(I){}
    inline int operator <(const interval rhs) const{
        return id<rhs.id;
    }
}a[maxm+5];

vector<interval>v[maxn+5];
int ans[maxn+5], n, m;

inline void input(){
    n=readin(1), m=readin(1);
    int l, r;
    rep(i, 1, m){
        l=readin(1), r=readin(1);
        a[i]=interval(l, r, i);
        v[r-l+1].push_back(a[i]);
    }
}

namespace sgtre{
    struct node{
        int ls, rs, mn;
        node(){ ls=rs=0, mn=inf; }
    }tre[maxn*250+5];
    int ncnt=0;
    #define ls tre[i].ls
    #define rs tre[i].rs
    #define mid ((l+r)>>1)
    inline int newnode(){
        assert(ncnt<=maxn*250);
        return ++ncnt;
    }
    void modify(int& i, int l, int r, int p, int v){
        if(!i) i=newnode();
        getmin(tre[i].mn, v);
        if(l==r) return;
        if(p<=mid) modify(ls, l, mid, p, v);
        else modify(rs, mid+1, r, p, v);
    }
    int query(int i, int l, int r, int L, int R){
        if(!i) return inf;
        if(L<=l && r<=R) return tre[i].mn;
        int ret=inf;
        if(L<=mid) ret=query(ls, l, mid, L, R);
        if(mid<R) getmin(ret, query(rs, mid+1, r, L, R));
        return ret;
    }
    #undef mid
    #undef ls
    #undef rs
}

namespace saya{
    int rt[maxn<<1|1];
    #define mid ((l+r)>>1)
    #define __i (((l)+(r))|((l)!=(r)))
    void modify(int l, int r, int L, int R, int v){
        sgtre::modify(rt[__i], 1, n, R, v);
        if(l==r) return;
        if(L<=mid) modify(l, mid, L, R, v);
        else modify(mid+1, r, L, R, v);
    }
    int query(int l, int r, int L, int R){
        if(L<=l && r<=R) return sgtre::query(rt[__i], 1, n, L, R);
        int ret=inf;
        if(L<=mid) getmin(ret, query(l, mid, L, R));
        if(mid<R) getmin(ret, query(mid+1, r, L, R));
        return ret;
    }
    #undef mid
    #undef __i
}

int res;

void solve(int l, int r){
    if(l>r) return;
    int ret=saya::query(1, n, l, r);
    if(ret>m) return; // it's not @p n but @p m
    res+=a[ret].r-a[ret].l+1;
    solve(l, a[ret].l-1); solve(a[ret].r+1, r);
}

signed main(){
    input();
    drep(i, n, 1){
        for(interval cur: v[i])
            saya::modify(1, n, cur.l, cur.r, cur.id);
        res=0; solve(1, n);
        ans[i]=res;
    }
    rep(i, 1, n) writc(ans[i]);
    return 0;
}

肆、关键的地方 ¶

合法区间的数量不超过 \(n\ln n\) && 对于区间覆盖问题的区间递归处理方法。

posted @ 2021-06-03 17:59  Arextre  阅读(63)  评论(0编辑  收藏  举报