这里介绍一种 vector 写法(来自某位 dalao)
首先,用 l , r , x l,r,x l,r,x 三个信息分别维护区间左右端点,和值。用vector 存这个结构体(显然的
struct Seg{int l,r; ll v;};
vector<Seg> vec;
那么当要涉及的区间跨越了这结构上的区间时候,就要进行区间分裂了。
-
假设要分裂开的区间是 [ u , v ] [u,v] [u,v],如果存在 l , r l,r l,r(在一个Seg内) 使得 l < u ≤ r l<u\leq r l<u≤r,此时就可以将 [ l , r ] [l,r] [l,r] 分裂为 [ l , u − 1 ] , [ u , r ] [l,u-1],[u,r] [l,u−1],[u,r] 两个区间。(对于 v v v 的处理之后再做)。
-
若 ∃ [ l , r ] ∣ ( l ≤ v < r ) \exists [l,r]|(l\leq v<r) ∃[l,r]∣(l≤v<r),则 [ l , r ] → [ l , v ] , [ v + 1 , r ] [l,r]\to[l,v],[v+1,r] [l,r]→[l,v],[v+1,r]。这样保证了区间的完整性。
-
如果 对于操作1 不存在这样的 [ l , r ] [l,r] [l,r],则一定存在 l = u l=u l=u 或者 l l l 超过了左右边界;如果 对于操作2 不存在这样的 [ l , r ] [l,r] [l,r],则一定存在 r = v r=v r=v 或者 r r r 超过了左右边界。事实上,由于取模的原因, l , r l,r l,r 不可能越界。故此时无需分裂区间,直接加入即可。
对于步骤1,在此位置的 l l l 改为 u u u,并在这前面 insert 进 { p a s t l , u − 1 , v a l } \{pastl,u-1,val\} {pastl,u−1,val} 即可。 p a s t l pastl pastl 即 l l l 修改前的值。
这里要注意了,在 O ( n ) O(n) O(n) 遍历的方法里,步骤2能不能直接从步骤1后面继续找?乍一看觉得可以,但实测是过不了的,再往前退 2 2 2 步找也是不行。这其实因为发生了说明3里的情况。步骤1的指针都跑到数组末尾了,这当然是不行的。所以要加入判断,如果找到了对应的 [ l , r ] [l,r] [l,r] ,则可以从这里继续,反之则需要从开头找(其实步骤1也可以结合二分 lower_bound ,这样就 O ( l o g n ) O(logn) O(logn) 找了,步骤二就可以直接往后推)。
示例代码:
void split(int l,int r){
for(auto it=vec.begin();it!=vec.end();++it){
auto[u,v,w]=*it; if(u<l&&l<=v){
it->l=l,vec.insert(it,{u,l-1,w}); break; }
//由于没有引用,所以可以直接这么写
}
for(auto it=vec.begin();it!=vec.end();++it){
auto[u,v,w]=*it; if(u<=r&&r<v){
it->l=r+1,vec.insert(it,{u,r,w}); break; }
}
}
对于操作1,先 split 开 l , r l,r l,r,之后模拟即可
split(l,r);
for(auto&[u,v,w]:vec)
if(l<=u&&v<=r) w+=x;
对于操作2,先 split 开 l , r l,r l,r,之后找到对应位置,擦掉对应区间信息,再填上一个新的即可
split(l,r);
for(wz1=0;l>vec[wz1].l;) ++wz1;
for(wz2=wz1;wz2<vec.size()&&vec[wz2].r<=r;) ++wz2;
vec.erase(vec.begin()+wz1,vec.begin()+wz2); //[l,r)
vec.insert(vec.begin()+wz1,{l,r,x});
对于操作3,找区间第 x x x 小。这个需要一些小 trick。建一个 vector 存这段区间的信息。按照值从小到大排序,然后减去长度,遍历即可。
vector<pair<ll,int>> px;
for(auto[u,v,w]:vec)
if(u<=r&&l<=v) px.push_back({w,min(v,r)-max(u,l)+1});
sort(px.begin(),px.end());
for(auto[p,q]:px)
if((x-=q)<=0){cout<<p<<'\n'; break;}
操作4,只要 Seg区间 与 [ l , r ] [l,r] [l,r] 有交集,即可计算答案。
y=rnd()%vmax+1,ans=0;
for(auto[u,v,w]:vec)
if(u<=r&&l<=v)
(ans+=1ll*(min(v,r)-max(u,l)+1)*qpow(w,x,y)%y)%=y;
cout<<ans<<'\n';
结语:这份代码其实还不够完美,可以用二分找第一个 ≥ l \ge l ≥l 的数,也可以在遍历到 r r r 右边时,立马 break。但这种思路很好理解,每次 O ( n 区间长度 ) O(\frac{n}{区间长度}) O(区间长度n) 的复杂度可以接受。
想看代码实现细节的,戳这里 完整代码