uoj#164. 【清华集训2015】V
思路:科学的题面:
请你写一个数据结构支持以下功能:
1:区间[l,r]加x
2:区间[l,r]减x并和0取max
3:区间覆盖
4:单点询问
5:单点历史最大值询问
线段树维护分段函数
标记就是一个二元组(a,b)表示标记生效后x=max(x+a,b)
1操作就是打(x,0)的标记
2就是(-x,0)
3就是(-inf,v)
我们手推一下就可以发现这个标记是满足结合律和封闭性的
然后两个标记怎么合并呢?
g(f(x))=max(x+max(fa+ga,-inf),max(fb+ga,gb))(打f标记的时间在前,打g标记在后)
中间和-inf取max是为了不使多个-inf加爆了
对于历史最大值,我们要记录的是历史最大标记而不是直接在每个点记录历史最大值
为什么是这样的?
假设我们进行一次区间赋为inf的操作,接着有全部赋为0,标记还没来得及下传更新历史最大值就被后一个标记cha了
所以每个点记录历史最大值是错的
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define PI pair<long long,long long> #define mp(a,b) make_pair(a,b) #define fi first #define se second typedef long long ll; const int maxn=500010; const ll inf=4557430888798830399ll; using namespace std; int n,m,a[maxn]; PI max(PI a,PI b){return mp(max(a.fi,b.fi),max(a.se,b.se));} PI operator +(PI f,PI g){return mp(max(f.fi+g.fi,-inf),max(f.se+g.fi,g.se));} struct Tsegment{ #define ls (p<<1) #define rs ((p<<1)|1) #define mid ((l+r)>>1) PI now[maxn<<2],ever[maxn<<2]; void add(int p,int ch){ ever[ch]=max(ever[ch],now[ch]+ever[p]); now[ch]=now[ch]+now[p]; } void down(int p){ add(p,ls),add(p,rs); now[p]=ever[p]=mp(0,0); } void build(int p,int l,int r){ if (l==r){now[p]=ever[p]=mp(a[l],0);return;} build(ls,l,mid),build(rs,mid+1,r); } void modify(int p,int l,int r,int a,int b,PI v){ //printf("p=%d l=%d r=%d a=%d b=%d\n",p,l,r,a,b); if (l==a&&r==b){ now[p]=now[p]+v; ever[p]=max(ever[p],now[p]); return; } down(p); if (b<=mid) modify(ls,l,mid,a,b,v); else if (a>mid) modify(rs,mid+1,r,a,b,v); else modify(ls,l,mid,a,mid,v),modify(rs,mid+1,r,mid+1,b,v); } ll query(int p,int l,int r,int x,int op){ if (l==r){ if (op) return max(ever[p].fi,ever[p].se); else return max(now[p].fi,now[p].se); } down(p); if (x<=mid) return query(ls,l,mid,x,op); else return query(rs,mid+1,r,x,op); } void cover(int l,int r,int v){modify(1,1,n,l,r,mp(-inf,v));} void inc(int l,int r,int v){modify(1,1,n,l,r,mp(v,0));} ll query(int x,int op){return query(1,1,n,x,op);} }T; int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); T.build(1,1,n); for (int i=1,l,r,x,op;i<=m;i++){ scanf("%d",&op); if (op==1) scanf("%d%d%d",&l,&r,&x),T.inc(l,r,x); else if (op==2) scanf("%d%d%d",&l,&r,&x),T.inc(l,r,-x); else if (op==3) scanf("%d%d%d",&l,&r,&x),T.cover(l,r,x); else if (op==4) scanf("%d",&x),printf("%lld\n",T.query(x,0)); else if (op==5) scanf("%d",&x),printf("%lld\n",T.query(x,1)); else if (op==6){ for (int i=1;i<=n;i++) printf("%lld ",T.query(i,0));puts(""); } else if (op==7){ for (int i=1;i<=n;i++) printf("%lld ",T.query(i,1));puts(""); } } return 0; } /* 5 6 1 2 3 4 5 2 1 3 2 //0 0 1 4 5 4 1 //0 1 1 4 1 //1 1 2 5 6 5 3 //3 3 1 5 4 //4 4 4 4 4 4 2 //4 */