[DS记录]P6792 [SNOI2020] 区间和

command_block

2021-07-18 23:15:26

Personal

**题意** : 维护一个长为 $n$ 的序列 $A$ (可能有负数) ,支持下列操作 : - 给出 $l,r,x$ ,将 $k\in [l,r]$ 的 $a_k\leftarrow \max(a_k,x)$ - 求区间 $[l,r]$ 的最大子段和。 $n\leq 10^5,m\leq 2\times 10^5$ ,时限$\texttt{3s}$。 ------------ > 感谢 @Fuyuki 的指导。 根据区间取 $\max$ 区间求和的经典 $\text{Seg-Beats}$ ,考虑维护最小值。 记 区间和、区间最小值、区间最小值个数 为 $s,x,c$。 记 最大前缀、最大后缀、最大子段和 为 $ls,rs,ms$。 一并维护 $ls,rs,ms$ 中含有的最小值个数,记为 $lc,rc,mc$ 。在仅修改最小值时就能快速计算影响。 但是,即使仅修改了最小值,上述三者的最优方案也有可能发生变化。 我们为 最大前缀、最大后缀 记一个“最小击破时间”,即最小值增大到多少才能使得某个方案变化。记为 $t$。(注意到 $ms$ 的方案变化一定是因为 $ls$ 或 $rs$ 的方案变化,无需考虑其击破时间) 在合并节点 $l,r$ 时 : $u.t\leftarrow l.t,r.t$ 。 - 若 $u.ls=l.s+r.ls$ ,则随着最小值的增大, $l.s+r.ls$ 一定比 $l.ls$ 大,无贡献。 - 若 $u.ls=l.ls$ ,则 $l.s+r.ls$ 击破 $l.ls$ 所需增量为 $\Big\lceil\dfrac{l.ls-l.s-r.ls}{l.c+r.lc-l.lc}\Big\rceil$ $u.rs$ 的贡献也是类似的,不再赘述。 这样,我们就容易判断修改是否会影响 $ls,rs,ms$ 的方案,并适时暴力递归。 分析复杂度。 由于我们只会增大权值, $ls,rs$ 的长度只会单调增加,于是只会发生 $O(n\log n)$ 次“击破”事件。 对于每次“击破”事件,需要来到对应节点,这花费 $O(\log n)$ 的递归复杂度。 总复杂度为 $O(n\log^2n+m\log n)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 200500 using namespace std; const int INF=1000000010; struct Node{ int x,c,tg,lc,rc,mc,t;ll s,ls,rs,ms; // t 也考虑了区间次小值 inline void Init(int a){ s=x=a;c=1; if (a<=0)ls=rs=ms=lc=rc=mc=t=0; else {ls=rs=ms=a;lc=rc=mc=1;t=INF;} } inline void ladd(int tg2){ tg=tg2;swap(x,tg2);tg2=x-tg2; s+=1ll*tg2*c;ls+=1ll*tg2*lc; rs+=1ll*tg2*rc;ms+=1ll*tg2*mc; } }a[1<<18|500]; inline void up(int u){ int l=u<<1,r=u<<1|1,c1,c2;ll s1,s2; a[u].s=a[l].s+a[r].s; a[u].x=min(a[l].x,a[r].x); bool fl=(a[l].x==a[u].x),fr=(a[r].x==a[u].x); a[u].c=fl*a[l].c+fr*a[r].c; a[u].t=min( min(a[l].t,a[r].t), min(fl ? a[l].t : a[l].x-1,fr ? a[r].t : a[r].x-1) ); c1=fl*a[l].lc;c2=fl*a[l].c+fr*a[r].lc; s1=a[l].ls;s2=a[l].s+a[r].ls; if (s2>s1){a[u].ls=s2;a[u].lc=c2;} else { a[u].ls=s1;a[u].lc=c1; if (c2>c1)a[u].t=min((ll)a[u].t,a[u].x+(s1-s2)/(c2-c1)); } c1=fr*a[r].rc;c2=fr*a[r].c+fl*a[l].rc; s1=a[r].rs;s2=a[r].s+a[l].rs; if (s2>s1){a[u].rs=s2;a[u].rc=c2;} else { a[u].rs=s1;a[u].rc=c1; if (c2>c1)a[u].t=min((ll)a[u].t,a[u].x+(s1-s2)/(c2-c1)); } if (a[l].rs+a[r].ls>max(a[l].ms,a[r].ms)){ a[u].ms=a[l].rs+a[r].ls; a[u].mc=fl*a[l].rc+fr*a[r].lc; }else if (a[l].ms>a[r].ms) {a[u].ms=a[l].ms;a[u].mc=fl*a[l].mc;} else {a[u].ms=a[r].ms;a[u].mc=fr*a[r].mc;} } int x[MaxN]; void build(int l,int r,int u) { a[u].tg=INF; if (l==r){a[u].Init(x[l]);return ;} int mid=(l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); up(u); } inline void ladd(int u){ if (a[u].tg!=INF){ if (a[u].tg>a[u<<1].x) a[u<<1].ladd(a[u].tg); if (a[u].tg>a[u<<1|1].x) a[u<<1|1].ladd(a[u].tg); a[u].tg=INF; } } int wfl,wfr,wfc;ll tms,trs; void chg(int l,int r,int u) { if (wfc<=a[u].x)return ; if (wfl<=l&&r<=wfr&&wfc<=a[u].t) {a[u].ladd(wfc);return ;} if (l==r){a[u].Init(wfc);return ;} int mid=(l+r)>>1;ladd(u); if (wfl<=mid)chg(l,mid,u<<1); if (mid<wfr)chg(mid+1,r,u<<1|1); up(u); } void qry(int l,int r,int u) { if (wfl<=l&&r<=wfr){ tms=max(max(tms,a[u].ms),trs+a[u].ls); trs=max(trs+a[u].s,a[u].rs); return ; }int mid=(l+r)>>1;ladd(u); if (wfl<=mid)qry(l,mid,u<<1); if (mid<wfr)qry(mid+1,r,u<<1|1); } int n,m; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++)scanf("%d",&x[i]); build(1,n,1); for (int i=1,op;i<=m;i++){ scanf("%d%d%d",&op,&wfl,&wfr); if (op==0){scanf("%d",&wfc);chg(1,n,1);} else {tms=trs=0;qry(1,n,1);printf("%lld\n",tms);} }return 0; } ```