ABC389F 题解

ABC389F 题解

Keyword

转化,值域区间加减 \(1\)

题意

不多赘述了,直接搜索题面即可。

分析

一个很感性的理解就是:初始分数越高,最后的分数也会越高(至少不会更低)

这个特性是因为,每次得分最多也就能 \(+1\) 。先不论这道题,来看一个很基础的数学模型。

有一个 若干个数组成的数组 \({a_n}\) ,其中每个元素都有一个对应的 \(rank\)。我们对其中的元素进行任意次操作,每一次取出\([l_i,r_i]\) 的元素,并进行 \(+-1\) ,那么操作完成之后每个元素对应的 \(rank\) 实际上是不会发生改变的(容易理解)。

这其实在 \(CF980\) 左右的场次(记不太清了)出过一道 \(B\) 题,同样的背景,只询问操作之后的最值,那么只用关注单点即可。

回到这道题上来,我们定义 \(f_i\)起始分数为 \(i\) ,最后能得到的对应分数 ,首先 \(f\) 本身是单调增加的,我们会进行若干次对 \([l_i,r_i]\) 中的数 \(+1\) 的操作,但是无论进行多少次,这个数组本身一定会保持单调不降(前面已经说明过了)。

所以对于每一次操作,我们二分一下就可以得知操作的左右端点在哪里,然后套一个数据结构就可以 \(O(n\log^2 n)\) 通过了。

但是这并不是最优秀的复杂度,线段树二分可以做到 \(O(n\log n)\) ,然而我还没学,这里挖个坑。。。

Code_naive

//线段树+二分 的 O(n \log^2 n) 写法 ,并非最优秀的 线段树二分 (n\logn)
//然而这种写法转差分之后就可以树状数组了 其实根本用不上线段树
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
struct SegmentTree
{
    int l,r,val,tag;
    #define L tr[x].l
    #define R tr[x].r
}tr[N<<2];
inline int calc(int x){return tr[x].val+(tr[x].r-tr[x].l+1)*tr[x].tag;}
inline void pd(int x){tr[x<<1].tag+=tr[x].tag,tr[x<<1|1].tag+=tr[x].tag,tr[x].tag=0;}
inline void pu(int x){tr[x].val=calc(x<<1)+calc(x<<1|1);}
inline void build(int x,int l,int r)
{
    tr[x].l=l,tr[x].r=r;
    if(l==r)
    {
        tr[x].val=l;
        return ;
    }
    int mid=l+r>>1;
    build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    pu(x);
}
inline void modify(int x,int l,int r,int v)
{
    if(l<=L&&R<=r)
    {
        tr[x].tag+=v;
        return ;
    }
    pd(x);
    int mid=L+R>>1;
    if(l<=mid)modify(x<<1,l,r,v);
    if(r>mid)modify(x<<1|1,l,r,v);
    pu(x);
}
inline int query(int x,int l,int r)
{
    if(l<=L&&R<=r)return calc(x);
    pd(x);
    int ans=0;
    int mid=L+R>>1;
    if(l<=mid)ans+=query(x<<1,l,r);
    if(r>mid)ans+=query(x<<1|1,l,r);
    pu(x);
    return ans;
}
struct Query
{
    int l,r;
}q[N];
int n,m;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n;
    build(1,1,N);
    for(int i=1;i<=n;++i)
    {
        cin>>q[i].l>>q[i].r;
        int ml,mr;
        int l=1,r=N;
        while(l<r)
        {
            int mid=l+r>>1;
            if(query(1,mid,mid)<q[i].l)l=mid+1;
            else r=mid;
        }
        ml=l;
        l=1,r=N;
        while(l<r)
        {
            int mid=(l+r+1)>>1;
            if(query(1,mid,mid)>q[i].r)r=mid-1;
            else l=mid;
        }
        mr=l;
        modify(1,ml,mr,1);
    }
    cin>>m;
    int x;
    while(m--)
    {
        cin>>x;
        cout<<query(1,x,x)<<'\n';
    }
    return 0;
}

Code_better

posted @ 2025-02-24 22:25  Hanggoash  阅读(14)  评论(0)    收藏  举报
动态线条
动态线条end