[CodeChef] The Street

给定两个长度为n的数列A和B,开始数组A中每一项值为0,数组B中每一项值为负无穷大。接下来有m次操作:1.数组A区间加一个等差数列;2.数组B区间对一个等差数列取max;3.询问ai+bi的值。n<=1e9,m<=3e5。

这道题做了两天。。告诉我:要随着递归函数变的变量,千万不要放在全局。。

  1 #include <cstdio>
  2 using namespace std;
  3 
  4 //#bug2:LL
  5 typedef long long LL;
  6 const LL maxn=1e9+5, maxm=3e5+5;
  7 const LL INF=1ll<<62;
  8 //#bug10:INF不够大
  9 LL n, m, op, l2s, cnt;
 10 LL u, v, a, b, x; //注意a,b是一直不变的
 11 LL ll, rr;
 12 //注意值最多可以加到LL外!
 13 struct node{
 14     LL b1, change1; //max
 15     LL a1;
 16     LL a2, b2; //add
 17     LL lc, rc;
 18 }seg[maxm*31*2];
 19 
 20 inline LL abs(LL x) { return x>0?x:-x; }
 21 inline LL max(LL x, LL y) { return x>y?x:y; }
 22 
 23 void flag(LL now, LL a1, LL b1, LL a2, LL b2);
 24 //给孩子打上标记
 25 void push_down(LL &now, LL l, LL r, LL a, LL b){
 26     //#bug4:忘记新建结点
 27     if (!now) now=++cnt, seg[now].a1=-INF;
 28     //#bug5:历史遗留问题(话说这个pushdown有点多余)
 29     //#bug7:b值有变化。。现在更新的b值不与下传的b值相同
 30     //更新的是目前uvab,下传的是这个结点的ab。(在下面修正)
 31     flag(now, seg[now].a1, seg[now].a1+(r-l)*seg[now].b1,
 32          a, a+(r-l)*b);
 33 }
 34 //取max,当然是标记永久化!
 35 void flag(LL now, LL a1, LL b1, LL a2, LL b2){
 36     //有个新来的数列的公差b,是在外面的
 37     //如果a1,b1没有值,或者盖住,那就直接赋值
 38     //bug#6:a1没有值的时候应该是负无穷大
 39     if (seg[now].a1==-INF||(a1<=a2&&b1<=b2)){
 40         seg[now].a1=a2, seg[now].b1=b;
 41         seg[now].change1=1;
 42         return;
 43     }
 44     if (a1>a2&&b1>b2) return;
 45     //那么现在必定有区间内交点
 46     LL mid=(ll+rr)>>1;
 47     LL nowl, nowr;
 48     if (abs(a1-a2)<abs(b1-b2)){ //交点在左边
 49         //如果a1右半部分更小,就全部赋值(#bug7:注意下传标记!)
 50         if (a1>a2) {
 51             LL tmp=b; b=seg[now].b1;
 52             //它的孩子替换成自己
 53             //bug#9:l和r不对
 54             nowl=ll, nowr=rr; rr=mid;
 55             push_down(seg[now].lc, ll, mid, seg[now].a1, seg[now].b1);
 56             rr=nowr; ll=mid+1;
 57             push_down(seg[now].rc, mid+1, rr,
 58                       seg[now].a1+(mid+1-nowl)*seg[now].b1, seg[now].b1);
 59             ll=nowl;
 60             b=tmp;
 61             seg[now].a1=a2, seg[now].b1=b, seg[now].change1=1;
 62         }
 63         //反之,把新数列的左半边往下传(它的孩子替换成新的)
 64         //#bug7:忽略了等于的情况(证伪)
 65         if (a1<=a2) { //#bug8:b不对。就是原来的
 66             nowr=rr; rr=mid;
 67             push_down(seg[now].lc, ll, mid, a2, b);
 68             rr=nowr;
 69         }
 70     } else {
 71         //与之前相同(它的孩子替换成新的)
 72         if (a1>=a2) { //同bug8
 73             //#bug9:下面的式子还是要用原来的ll
 74             nowl=ll; ll=mid+1;
 75             push_down(seg[now].rc, mid+1, rr, a2+(mid+1-nowl)*b, b);
 76             ll=nowl;
 77         }
 78         //同bug7(证伪)
 79         if (a1<a2) {
 80             LL tmp=b; b=seg[now].b1;
 81             //它的孩子替换成自己(注意下传标记!)
 82             nowl=ll, nowr=rr; rr=mid;
 83             push_down(seg[now].lc, ll, mid, seg[now].a1, seg[now].b1);
 84             rr=nowr; ll=mid+1;
 85             push_down(seg[now].rc, mid+1, rr,
 86                       seg[now].a1+(mid+1-nowl)*seg[now].b1, seg[now].b1);
 87             ll=nowl;
 88             b=tmp;
 89             seg[now].a1=a2, seg[now].b1=b, seg[now].change1=1;
 90         }
 91     }
 92 }
 93 void modify1(LL &now, LL l, LL r){
 94     if (!now) now=++cnt, seg[now].a1=-INF; //用引用新建结点,这样空的不会被识别
 95     l2s=a+(l-u)*b; //l这个x坐标上的l2的值
 96     ll=l, rr=r;
 97     LL mid=(l+r)>>1;
 98     LL nowl, nowr;
 99     //如果修改过,必须再这里先push一下,不然flag的时候,可能会覆盖
100     if (seg[now].change1){
101         LL tmp=b; b=seg[now].b1;
102         //它的孩子替换成自己
103         nowl=ll, nowr=rr; rr=mid;
104         push_down(seg[now].lc, l, mid, seg[now].a1, seg[now].b1);
105         rr=nowr, ll=mid+1;
106         push_down(seg[now].rc, mid+1, r,
107                   seg[now].a1+(mid+1-nowl)*seg[now].b1, seg[now].b1);
108         ll=nowl;
109         b=tmp;
110         //#bug3:mid+1全没加 (还是mid!!)
111         seg[now].change1=0;
112     }
113     if (l>=u&&r<=v){
114         //给出俩直线开始结束,修改下传结点。只要判断交点位置
115         flag(now, seg[now].a1, seg[now].a1+(r-l)*seg[now].b1,
116              l2s, l2s+(r-l)*b);
117         return;
118     }
119     if (mid>=u) modify1(seg[now].lc, l, mid);
120     if (mid<v) modify1(seg[now].rc, mid+1, r);
121 }
122 //这个。。可以差分
123 void modify2(LL &now, LL l, LL r){
124     if (!now) now=++cnt, seg[now].a1=-INF; //这样空的不会被识别
125     l2s=a+(l-u)*b;
126     if (l>=u&&r<=v){
127         seg[now].a2+=l2s, seg[now].b2+=b;
128         return;
129     }
130     LL mid=(l+r)>>1;
131     if (mid>=u) modify2(seg[now].lc, l, mid);
132     if (mid<v) modify2(seg[now].rc, mid+1, r);
133 }
134 LL q1(LL now, LL l, LL r){ //只有当路径上全部都是-INF时,这个东西不存在
135     if (!now) return -INF;
136     LL mid=(l+r)>>1, v=seg[now].a1+(x-l)*seg[now].b1;
137     if (mid>=x) return max(v, q1(seg[now].lc, l, mid));
138     else return max(v, q1(seg[now].rc, mid+1, r));
139 }
140 LL q2(LL now, LL l, LL r){ //差分。。
141     if (!now) return 0;
142     LL mid=(l+r)>>1, v=seg[now].a2+(x-l)*seg[now].b2;
143     if (mid>=x) return v+q2(seg[now].lc, l, mid); //bug#1,mid>x
144     else return v+q2(seg[now].rc, mid+1, r);
145 }
146 
147 int main(){
148     scanf("%lld%lld", &n, &m);
149     LL root=++cnt; seg[root].a1=-INF;
150     for (LL i=0; i<m; ++i){
151         scanf("%lld", &op);
152         //这里把a看成开始,b看成公差!
153         if (op<3) {
154             scanf("%lld%lld%lld%lld", &u, &v, &a, &b);
155             LL t=a; a=b; b=t;
156         }
157         if (op==1) modify1(root, 1, n);
158         if (op==2) modify2(root, 1, n);
159         if (op==3){
160             scanf("%lld", &x);
161             LL query1=q1(1, 1, n), query2=q2(1, 1, n);
162             if (query1==-INF) { printf("NA
"); continue; }
163             printf("%lld
", query1+query2);
164         }
165     }
166     return 0;
167 }
原文地址:https://www.cnblogs.com/MyNameIsPc/p/7529935.html