洛谷题目传送门c++
考场上一眼看出这是个毒瘤线段树准备杠题,发现实在太难调了,被各路神犇虐哭qwq函数
考后看到各类优雅的暴力AC。。。。。。宝宝内心苦qwqui
题面里面是一堆乱七八糟的限制和性质,这时候须要冷静想一想有没有可利用的地方。蒟蒻一开始往势能线段树上面想了想。this
定义一个全局势能函数,为全部\(C_i<B_i\)的位置个数。注意两个操做的修改都不会小于原来的数。spa
一个是改\(A\),至关于对\(C\)进行区间设置,此时咱们每暴力找到一个原来\(C_i<B_i\)可是如今\(C_i\ge y\)的位置,就须要在线段树内跳\(\log\)层,再修改,势能函数就会降低。若是碰到那些修改之后对势能没有影响的子区间,就跳过而不继续暴力递归下去。指针
一个是改\(B\),是单点修改,线段树内跳\(\log\)层,势能函数至多加\(1\)。调试
因而,若是咱们可以维护信息,从而判断和控制哪里该暴力递归、哪里该跳过的话,咱们总的在线段树内跳的次数不会超过\((n+q)\log n\)。code
下面用a代替了\(C\),b代替了\(B\)。蒟蒻在线段树里维护了:递归
修改a的时候,利用单调不降的性质,咱们在线段树上先经过二分来对须要修改的若干个子树进行定位。对于当前子区间,若是当前设置值\(y\)比mb要小,那么设置对答案没有影响,直接打上区间设置标记后退出;不然继续递归直到找到叶子节点,进行修改后退出。ip
修改b就比较轻松,只要找到对应的叶子节点改完后一路回溯便可。
不少事都是提及来容易作起来难,这题也不例外。调试几乎花了整个晚上。要注意的细节不少,也只好本身仔细思考了。
时间复杂度\(O(n\log n+q\log^2n)\),上界很松。多出来的\(\log\)是区间改a时须要快速幂更新pa形成的。
跑了不到200ms,比什么树套树、分块仍是要好看一点,可是被暴力碾压也是有点无奈啊~
#include<bits/stdc++.h> #define RG register #define R RG int #define I inline #define G if(++ip==ie)fread(ip=buf,1,N,stdin) using namespace std; typedef long long LL; const LL N=1<<18,YL=1e9+7; char buf[N],*ie=buf+N,*ip=ie-1; int y,a[N]; I int in(){ G;while(*ip<'-')G; R x=*ip&15;G; while(*ip>'-'){x*=10;x+=*ip&15;G;} return x; } I LL qpow(RG LL b,R k){//快速幂 RG LL a=1; for(;k;k>>=1,(b*=b)%=YL) if(k&1)(a*=b)%=YL; return a; } struct Node{//我的认为写指针比较直观(貌似immortalCO也有这样的见解) Node*lc,*rc;bool la; LL pa,pb;int l,r,m,ma,mb,cnt; I void up(){//上传 pa=lc->pa*rc->pa%YL; pb=lc->pb*rc->pb%YL; ma=max(lc->ma,rc->ma); mb=min(lc->mb,rc->mb); cnt=lc->cnt+rc->cnt; } I void dn(){//下传区间设置标记 if(la){ lc->ma=rc->ma=ma; lc->la=rc->la=1;la=0; lc->pa=qpow(ma,lc->cnt); rc->pa=qpow(ma,rc->cnt); } } I void build(R s,R e){//建树 l=s;r=e;m=(s+e)>>1;la=0; if(s==e){ ma=a[l];mb=in(); (cnt=ma<mb)?(pa=ma,pb=1):(pa=1,pb=mb,mb=YL); return; } (lc=new Node)->build(l,m); (rc=new Node)->build(m+1,r); this->up(); } I void upda(){//区间修改a if(y<mb){//对区间势能没有影响 pa=qpow(ma=y,cnt);la=1; return; } if(l==r){//到了叶子节点 ma=y;pa=1;pb=mb;mb=YL; return; } this->dn(); lc->upda();rc->upda(); this->up(); } I void updb(R s){//单点更新b if(l==r){//仔细判断三种状况再修改 if(ma<pb)mb=y; else if(ma<y)pa=ma,pb=cnt=1,mb=y; else pb=y; return; } this->dn(); (s<=m?lc:rc)->updb(s); this->up(); } I void bound(R s){//线段树二分定位,注意细节 if(s==l&&ma<y)return this->upda(); if(l==r)return; this->dn(); if(lc->ma<y)rc->bound(m+1); lc->bound(s); this->up(); } }; int main(){ R n=in(),q=in(),op,x; for(R i=1;i<=n;++i)a[i]=max(a[i-1],in()); RG Node rt;rt.build(1,n); while(q--){ op=in();x=in();y=in(); op?rt.updb(x):rt.bound(x); printf("%lld\n",rt.pa*rt.pb%YL); } return 0; }