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
鸽
为什么要练,为什么要写?
引用一句让我幡然悔悟的话:
“练了不一定写的出来正解,不练一定写不出来正解”
本文来自博客园,作者:Hanggoash,转载请注明原文链接:https://www.cnblogs.com/Hanggoash/p/18735115

浙公网安备 33010602011771号