BZOJ题目传送门php
终于体会到初步掌握势能分析思想的重要性了。html
一开始看题,感受套路仍是很通常啊qwq。直接在线段树上维护最大值和最小值,每次递归更新的时候,若是不能彻底覆盖就暴力递归下去。挺好写的欸c++
鉴于上次写冒险常数太大的经历,蒟蒻此次来个码风奇特的指针线段树编程
#include<bits/stdc++.h> #define RG register #define R RG int #define G if(++ip==ie)fread(ip=buf,1,N,stdin) #define pushup \ s=lc->s+rc->s; \ mn=min(lc->mn,rc->mn); \ mx=max(lc->mx,rc->mx) #define pushdn \ if(ls!=INF)lc->lset(ls),rc->lset(ls),ls=INF;\ if(la)lc->ladd(la),rc->ladd(la),la=0 using namespace std; typedef long long LL; const int N=1<<20,INF=1e9; char buf[N],*ie=buf+N,*ip=ie-1; int op,x; inline int in(){ G;while(*ip<'-')G; RG bool f=*ip=='-';if(f)G; R x=*ip&15; G;while(*ip>'-'){x*=10;x+=*ip&15;G;} return f?-x:x; } struct Node{ Node*lc,*rc; int l,r,m,mn,mx,ls;LL s,la; void build(R b,R e){//建树 m=((l=b)+(r=e))>>1;ls=INF;la=0; if(b==e){ s=mn=mx=in();return; } (lc=new Node)->build(l,m); (rc=new Node)->build(m+1,r); pushup; } inline void lset(R x){//区间覆盖 s=(LL)x*(r-l+1);mn=mx=ls=x;la=0; } inline void ladd(R x){//区间加 s+=(LL)x*(r-l+1);mn+=x;mx+=x;la+=x; } void add(R b,R e){//操做1 if(l==b&&r==e)return this->ladd(x); pushdn; if(e<=m)lc->add(b,e); else if(b>m)rc->add(b,e); else lc->add(b,m),rc->add(m+1,e); pushup; } void upd(R b,R e){//操做2/3 if(op&1?mx<=x:mn>=x)return; if(l==b&&r==e&&(op&1?mn>=x:mx<=x))return this->lset(x); pushdn; if(e<=m)lc->upd(b,e); else if(b>m)rc->upd(b,e); else lc->upd(b,m),rc->upd(m+1,e); pushup; } LL qrys(R b,R e){//操做4 if(l==b&&r==e)return s; pushdn; if(e<=m)return lc->qrys(b,e); if(b> m)return rc->qrys(b,e); return lc->qrys(b,m)+rc->qrys(m+1,e); } int qrym(R b,R e){//操做5/6 if(l==b&&r==e)return op&1?mx:mn; pushdn; if(e<=m)return lc->qrym(b,e); if(b> m)return rc->qrym(b,e); if(op&1)return max(lc->qrym(b,m),rc->qrym(m+1,e)); return min(lc->qrym(b,m),rc->qrym(m+1,e)); } }; int main(){ RG Node rt;rt.build(1,in()); for(R m=in(),l,r;m;--m){ op=in();l=in();r=in(); if(op<=3)x=in(),op==1?rt.add(l,r):rt.upd(l,r); else if(op==4)printf("%lld\n",rt.qrys(l,r)); else printf("%d\n",rt.qrym(l,r)); } return 0; }
而后就过了?!而后拿了BZOJ rank1?!函数
而后这是暴力。ui
很显然咱们仍是要着眼于势能分析。下面开始瞎逼逼,只讨论取min,由于取max是一回事。this
随便定义一个线段树节点的势函数为其管辖区间内不一样数的个数。这样初始势函数总和就是\(O(n\log n)\)级别的。spa
若是只记区间最小值,那么显然若是作一次修改,它可能仍是最小值,势函数并无减少。.net
那要怎么好呢?记次小值!若是\(x\)大于最小值而小于次小值,那么咱们打上一个区间修改最小值的标记;若是\(x\)大于等于次小值,那么确定不一样数的个数会减小,此处每额外展开一次暴力递归势函数就会至少减少\(1\),复杂度就是对的了。指针
注意到这里要维护和,因而咱们在记最小值的时候顺便维护区间内最小值的个数。
若是只有区间min/max,那么复杂度就是一个\(\log\)的了。
但是这里的操做既有min又有max还有区间加,这时要另外分析。吉老师好像在论文里说有一个比较松的\(m\log^2\)上界,不过蒟蒻发现还不是很懂势能分析那一套理论就没法接着逼逼下去了。
蒟蒻注意到这样一句话
代码实现较长,但理解本作法后编程复杂度并不高。
然而——这就是一个能够拍几十次次次都WA,重构三次代码的线段树吗?
细节问题:
区间加,区间加最小值,区间加最大值按理来讲彷佛是独立的,但是蒟蒻重构了两边最后放区间加标记的代码就是调不出来,无奈之下最早放了区间加,而后又由于玄学问题爆int(原本应该只有区间和要开longlong的啊)
cz_xuyixuan队爷写的是先放加min/max再放区间加的。
区间加min的时候,可能还要考虑对区间最大值/次大值的影响(若是区间只有一个或者两个不一样的数的时候)。区间加max同理。
区间加min/max的时候,注意判断最值的子树来源后再放。
至于代码什么的会让你痛不欲生。。。蒟蒻硬是用define写函数把人人要写4KB+的代码缩成了3KB。。。此坑慎入!
由于不得已开longlong比
#include<bits/stdc++.h> #define RG register #define R RG int #define I inline void #define G if(++ip==ie)fread(ip=buf,1,N,stdin) using namespace std; typedef long long LL; const int N=1<<19,INF=1e9; char buf[N],*ie=buf+N,*ip=ie-1; int x; inline int in(){ G;while(*ip<'-')G; RG bool f=*ip=='-';if(f)G; R x=*ip&15; G;while(*ip>'-'){x*=10;x+=*ip&15;G;} return f?-x:x; } #define int LL//请无视 struct Node{ Node*lc,*rc; int l,r,m,mn1,mn2,mnc,mx1,mx2,mxc,lmn,lmx,lad,s; I up(){//维护最值和次值,当心点写 s=lc->s+rc->s; if(lc->mn1<rc->mn1) mn1=lc->mn1,mnc=lc->mnc,mn2=min(lc->mn2,rc->mn1); else if(lc->mn1>rc->mn1) mn1=rc->mn1,mnc=rc->mnc,mn2=min(rc->mn2,lc->mn1); else mn1=lc->mn1,mnc=lc->mnc+rc->mnc,mn2=min(lc->mn2,rc->mn2); if(lc->mx1>rc->mx1) mx1=lc->mx1,mxc=lc->mxc,mx2=max(lc->mx2,rc->mx1); else if(lc->mx1<rc->mx1) mx1=rc->mx1,mxc=rc->mxc,mx2=max(rc->mx2,lc->mx1); else mx1=lc->mx1,mxc=lc->mxc+rc->mxc,mx2=max(lc->mx2,rc->mx2); } I dnlad(R x){//区间加 mn1+=x;if(mn2!= INF)mn2+=x; mx1+=x;if(mx2!=-INF)mx2+=x; s+=x*(r-l+1);lad+=x; } I dnlmn(R x){//区间加最小值 if(mn1==mx1)mx1+=x;if(mn1==mx2)mx2+=x;//注意特判 s+=x*mnc;mn1+=x;lmn+=x; } I dnlmx(R x){//区间加最大值 if(mx1==mn1)mn1+=x;if(mx1==mn2)mn2+=x; s+=x*mxc;mx1+=x;lmx+=x; } I dn(){//放标记 if(lad)lc->dnlad(lad),rc->dnlad(lad),lad=0; if(lmn){ if(lc->mn1<=rc->mn1)lc->dnlmn(lmn); if(lc->mn1>=rc->mn1)rc->dnlmn(lmn); lmn=0; } if(lmx){ if(lc->mx1>=rc->mx1)lc->dnlmx(lmx); if(lc->mx1<=rc->mx1)rc->dnlmx(lmx); lmx=0; } } I build(R b,R e){//建树 m=((l=b)+(r=e))>>1;lmn=lmx=lad=0; if(l==r){ s=mn1=mx1=in();mnc=mxc=1; mn2=INF;mx2=-INF; return; } (lc=new Node)->build(l,m); (rc=new Node)->build(m+1,r); this->up(); } #define sum(x,y) x+y//修改函数模板 #define upd(Fun,Lim,Req,Tag) \ I Fun(R b,R e){ \ if(Lim)return; \ if(l==b&&r==e Req)return this->Tag; \ this->dn(); \ if(e<=m)lc->Fun(b,e); \ else if(b>m)rc->Fun(b,e); \ else lc->Fun(b,m),rc->Fun(m+1,e); \ this->up(); \ }//查询函数模板 #define qry(Fun,Typ,Ret,Opt) \ inline Typ Fun(R b,R e){ \ if(l==b&&r==e)return Ret; \ this->dn(); \ if(e<=m)return lc->Fun(b,e); \ if(b>m)return rc->Fun(b,e); \ return Opt(lc->Fun(b,m),rc->Fun(m+1,e));\ } upd(us,0,,dnlad(x)); upd(umn,mn1>=x,&&mn2>x,dnlmn(x-mn1)); upd(umx,mx1<=x,&&mx2<x,dnlmx(x-mx1)); qry(qs,LL,s,sum); qry(qmx,int,mx1,max); qry(qmn,int,mn1,min); }; #undef int int main(){ RG Node rt;rt.build(1,in()); for(R m=in(),op,l,r;m;--m){ op=in();l=in();r=in(); if(op<=3)x=in(); if(op==1)rt.us (l,r); if(op==2)rt.umn(l,r); if(op==3)rt.umx(l,r); if(op==4)printf("%lld\n",rt.qs (l,r)); if(op==5)printf("%lld\n",rt.qmx(l,r)); if(op==6)printf("%lld\n",rt.qmn(l,r)); } return 0; }