给定两个长度为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 }