题目描述
传送门
题目大意:维护一个长度为N的序列a,现在有三种操作:
1)给出参数U,V,C,将a[U],a[U+1],…,a[V-1],a[V]都赋值为C。
2)给出参数U,V,C,对于区间[U,V]里的每个数i,将a[i]赋值为max(a[i]+C,0)。
3)给出参数U,V,输出a[U],a[U+1],…,a[V-1],a[V]里值为0的数字个数。
题解
这道题比较麻烦的就是操作二中的max操作。
正常操作的线段树是不容易实现这种操作的,但是WC2015中提到的segment tree beats!可以实现这种区间取min,max的操作。并且证明每次操作的时间复杂度是
O(log2n)
的。
证明如下:
这道题有一个很关键的信息,刚开始没有用到。就是cover中的C一定是大于等于0的,也就是说序列中的最小值一定不小于0.为啥很关键?如果没有这个限制0可以处在区间中中间的位置,个数非常不好维护。有了这个限制,那么0如果在区间中出现,那么一定是作为区间最小值出现的,那么问题其实就巧妙的转换成了维护区间最小值的个数。
对于操作2,我们把他拆成两个操作,区间add和区间max。对于add,cover标记得处理比较老套。就是如果cover遇add,直接覆盖.add遇cover,直接把增量给cover,add不变。
对于max(v),我们考虑如何修改。如果区间的最小值>=v,那么该操作对区间没有影响直接舍弃。如果区间最小值
<
<script type="math/tex" id="MathJax-Element-10"><</script>v
<
<script type="math/tex" id="MathJax-Element-11"><</script>区间次小值,那么更改区间最小值和区间delta标记(delta标记维护的实际上类似历史max的东西,如果v能更新delta,说明他改变了区间最小值,这时对于子区间的最小值还没有取max。如果接着来了add操作,那么对于子区间的max影响实际上就是delta+add。所以delta会随着add的改变而改变需要动态的维护).如果区间次小值<=v,也就是区间中不止最小值需要改变,还有很多位置需要修改,只好dfs左右子树,说白了就是暴力修改。。。。
注意cover遇delta,直接把delta清掉即可。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define N 300005
using namespace std;
const LL inf=1e15;
struct data{
LL mn,cmn,cnt,zer,cover,add;
LL delta;
}tr[N*4];
int n,m;
LL val[N];
void update(data &now,data l,data r)
{
now.mn=min(l.mn,r.mn);
now.cnt=0;
if (l.mn==now.mn) now.cnt+=l.cnt;
if (r.mn==now.mn) now.cnt+=r.cnt;
now.zer=l.zer+r.zer;
now.cmn=inf;
if (l.mn!=now.mn) now.cmn=min(now.cmn,l.mn);
if (r.mn!=now.mn) now.cmn=min(now.cmn,r.mn);
now.cmn=min(now.cmn,l.cmn);
now.cmn=min(now.cmn,r.cmn);
}
void build(int now,int l,int r)
{
tr[now].cover=inf; tr[now].delta=-inf;
if (l==r) {
tr[now].mn=val[l];
tr[now].cnt=1;
tr[now].cmn=inf;
if (tr[now].mn==0) tr[now].zer=tr[now].cnt;
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(tr[now],tr[now<<1],tr[now<<1|1]);
}
void solve1(int now,int l,int r,LL v)
{
tr[now].mn+=v;
if (tr[now].cmn!=inf) tr[now].cmn+=v;
if (tr[now].cover!=inf) tr[now].cover+=v;
else tr[now].add+=v;
if (tr[now].delta!=-inf) tr[now].delta+=v;
}
void solve(int now,int l,int r,LL v)
{
tr[now].cover=v;
tr[now].mn=v; tr[now].cnt=r-l+1;
tr[now].cmn=inf;
tr[now].add=0; tr[now].delta=-inf;
}
void solve2(int now,int l,int r,LL v)
{
if (v>tr[now].mn) {
tr[now].mn=v;
tr[now].delta=max(v,tr[now].delta);
}
}
void pushdown(int now,int l,int r)
{
int mid=(l+r)/2;
if (tr[now].add) {
solve1(now<<1,l,mid,tr[now].add);
solve1(now<<1|1,mid+1,r,tr[now].add);
tr[now].add=0;
}
if (tr[now].cover!=inf) {
solve(now<<1,l,mid,tr[now].cover);
solve(now<<1|1,mid+1,r,tr[now].cover);
tr[now].cover=inf;
}
if (tr[now].delta!=-inf) {
solve2(now<<1,l,mid,tr[now].delta);
solve2(now<<1|1,mid+1,r,tr[now].delta);
tr[now].delta=-inf;
}
}
void cover(int now,int l,int r,int ll,int rr,LL v)
{
if (ll<=l&&r<=rr) {
solve(now,l,r,v);
return;
}
int mid=(l+r)/2;
pushdown(now,l,r);
if (ll<=mid) cover(now<<1,l,mid,ll,rr,v);
if (rr>mid) cover(now<<1|1,mid+1,r,ll,rr,v);
update(tr[now],tr[now<<1],tr[now<<1|1]);
}
void add(int now,int l,int r,int ll,int rr,LL v)
{
if (ll<=l&&r<=rr) {
solve1(now,l,r,v);
return;
}
int mid=(l+r)/2;
pushdown(now,l,r);
if (ll<=mid) add(now<<1,l,mid,ll,rr,v);
if (rr>mid) add(now<<1|1,mid+1,r,ll,rr,v);
update(tr[now],tr[now<<1],tr[now<<1|1]);
}
void mx(int now,int l,int r,int ll,int rr,int v)
{
if (ll<=l&&r<=rr){
if (tr[now].mn>=v) return;
if (tr[now].cmn>v) {
tr[now].delta=v;
tr[now].mn=v;
return;
}
int mid=(l+r)/2;
pushdown(now,l,r);
mx(now<<1,l,mid,ll,rr,v);
mx(now<<1|1,mid+1,r,ll,rr,v);
update(tr[now],tr[now<<1],tr[now<<1|1]);
return;
}
int mid=(l+r)/2;
pushdown(now,l,r);
if (ll<=mid) mx(now<<1,l,mid,ll,rr,v);
if (rr>mid) mx(now<<1|1,mid+1,r,ll,rr,v);
update(tr[now],tr[now<<1],tr[now<<1|1]);
}
LL query(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) return tr[now].mn==0?tr[now].cnt:0;
int mid=(l+r)/2; LL ans=0;
pushdown(now,l,r);
if (ll<=mid) ans+=query(now<<1,l,mid,ll,rr);
if (rr>mid) ans+=query(now<<1|1,mid+1,r,ll,rr);
return ans;
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&val[i]);
build(1,1,n);
for (int i=1;i<=m;i++){
int opt,l,r; LL v;
scanf("%d%d%d",&opt,&l,&r);
if (opt==1) scanf("%lld",&v),cover(1,1,n,l,r,v);
if (opt==2) { scanf("%lld",&v),add(1,1,n,l,r,v);
mx(1,1,n,l,r,0);
}
if (opt==3)
printf("%lld\n",query(1,1,n,l,r));
}
}