2320. [HZOI 2015]聪聪的世界
时间限制:6 s 内存限制:512 MB
【题目描述】
背景:
聪聪的性取向有问题。
题目描述:
聪聪遇到了一个难题:
给出一个序列a1…an,完成以下操作:
1 x 询问从x向左数第一个<ax的数;
2 x 询问从x向左数第一个>ax的数;
3 x 询问从x向右数第一个<ax的数;
4 x 询问从x向右数第一个>ax的数;
5 x y 交换ax与ay;
6 x y w 给ax…ay加上w;
7 x y w 给ax…ay减去w。
聪聪急切的想知道答案,因为他完成任务后就可以迎娶高富帅,出任CEO,走上人生巅峰,成为人生赢家!
请你帮帮他。
【输入格式】
第一行 n,m。
第二行 a1…an。
第三行到m+2行为以上七个操作。
【输出格式】
对于每个op>=1且op<=4输出一行表示答案,无解输出-1。
【样例输入】
5 5
8 2 0 0 9
1 2
5 1 3
7 1 3 1
4 2
1 1
【样例输出】
-1
7
-1
【提示】
10% n,m<=10000
40% n,m<=100000
100% n,m<=1000000
对于所有输入的数保证在[0,10^9]范围内
学长们出的题造的孽啊,貌似打法很多,我在这里只讲一下线段树解法。
首先先膜拜一下 神利·代目 stdafx.h,两位学长,我是在他们的引导下想出的O(log)时间复杂度内完成前4个操作的。
因为这四个操作本质一样,因此我们就只讲第一种操作。
请读者自己先思考5~10分钟,看看能否相出log复杂度的前四种操作打法,提醒一下,从根节点边向下边二分不靠谱。
开讲了,首先,线段树是一棵完全二叉树,因此它满足一个规律,兄弟节点的编号亦或1就是他自己,而它自己的编号/2就是他父亲的编号,因此我们完全可以用这个性质从下向上攀爬,再利用这个性质从上向下攀爬。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 long long n,m; 10 struct no{ 11 long long left,right; 12 long long mid,mn; 13 long long mx,lazy; 14 }node[4000004]; 15 long long a[1000005]; 16 long long dl[1000005]; 17 void build(long long left,long long right,long long x){ 18 node[x].left=left; 19 node[x].right=right; 20 if(left==right) 21 { 22 dl[left]=x; 23 node[x].mn=node[x].mx=a[left]; 24 return; 25 } 26 long long mid=(left+right)/2; 27 node[x].mid=mid; 28 build(left,mid,2*x); 29 build(mid+1,right,2*x+1); 30 node[x].mx=max(node[2*x].mx,node[2*x+1].mx); 31 node[x].mn=min(node[x*2].mn,node[2*x+1].mn); 32 } 33 void pushdown(long long x){ 34 if(node[x].lazy) 35 { 36 node[2*x].lazy+=node[x].lazy; 37 node[2*x+1].lazy+=node[x].lazy; 38 node[2*x].mx+=node[x].lazy; 39 node[2*x+1].mx+=node[x].lazy; 40 node[x*2].mn+=node[x].lazy; 41 node[2*x+1].mn+=node[x].lazy; 42 node[x].lazy=0; 43 } 44 } 45 long long get(long long left,long long right,long long x){ 46 if(node[x].left==node[x].right) 47 { 48 return node[x].mx; 49 } 50 pushdown(x); 51 long long mid=node[x].mid; 52 if(right<=node[x].mid) 53 return get(left,right,2*x); 54 else 55 return get(left,right,2*x+1); 56 } 57 void change(long long left,long long right,long long x,long long z){ 58 if(node[x].left==node[x].right) 59 { 60 node[x].mn=z; 61 node[x].mx=z; 62 return; 63 } 64 pushdown(x); 65 long long mid=node[x].mid; 66 if(right<=node[x].mid) 67 change(left,right,2*x,z); 68 else 69 change(left,right,2*x+1,z); 70 node[x].mx=max(node[2*x].mx,node[2*x+1].mx); 71 node[x].mn=min(node[2*x].mn,node[1+2*x].mn); 72 } 73 void add(long long left,long long right,long long x,long long z){ 74 if(node[x].left==left&&node[x].right==right) 75 { 76 node[x].mx+=z; 77 node[x].mn+=z; 78 node[x].lazy+=z; 79 return; 80 } 81 pushdown(x); 82 long long mid=node[x].mid; 83 if(right<=mid) 84 add(left,right,2*x,z); 85 else if(left>mid) 86 add(left,right,2*x+1,z); 87 else 88 add(left,mid,2*x,z),add(mid+1,right,2*x+1,z); 89 node[x].mx=max(node[2*x].mx,node[2*x+1].mx); 90 node[x].mn=min(node[x*2].mn,node[2*x+1].mn); 91 } 92 long long que_ls(long long x,long long z,long long buf){ 93 if(node[x].left==node[x].right) 94 return node[x].left; 95 if(node[2*x+1].mn+buf+node[x].lazy<z) 96 return que_ls(2*x+1,z,buf+node[x].lazy); 97 else 98 return que_ls(2*x,z,buf+node[x].lazy); 99 } 100 long long get_ls(long long x,long long z){ 101 if(x==1) 102 return -1; 103 if(!(x%2)) 104 return get_ls(x/2,z+node[x].lazy); 105 else 106 { 107 if(z+node[x].lazy>node[x^1].mn) 108 return que_ls(x^1,z+node[x].lazy,0); 109 else 110 return get_ls(x/2,z+node[x].lazy); 111 } 112 } 113 long long que_lb(long long x,long long z,long long buf){ 114 if(node[x].left==node[x].right) 115 return node[x].left; 116 if(node[2*x+1].mx+buf+node[x].lazy>z) 117 return que_lb(x*2+1,z,buf+node[x].lazy); 118 else 119 return que_lb(x*2,z,buf+node[x].lazy); 120 } 121 long long get_lb(long long x,long long z){ 122 if(x==1) 123 return -1; 124 if(!(x%2)) 125 return get_lb(x/2,z+node[x].lazy); 126 else 127 { 128 if(z+node[x].lazy<node[x^1].mx) 129 return que_lb(x^1,z+node[x].lazy,0); 130 else 131 return get_lb(x/2,z+node[x].lazy); 132 } 133 } 134 long long que_rs(long long x,long long z,long long buf){ 135 if(node[x].left==node[x].right) 136 return node[x].left; 137 if(node[2*x].mn+buf+node[x].lazy<z) 138 return que_rs(x*2,z,buf+node[x].lazy); 139 else 140 return que_rs(x*2+1,z,buf+node[x].lazy); 141 } 142 long long get_rs(long long x,long long z){ 143 if(x==1) 144 return -1; 145 if(x%2) 146 return get_rs(x/2,z+node[x].lazy); 147 else 148 { 149 if(z+node[x].lazy>node[x^1].mn) 150 return que_rs(x^1,z+node[x].lazy,0); 151 else 152 return get_rs(x/2,z+node[x].lazy); 153 } 154 } 155 long long que_rb(long long x,long long z,long long buf){ 156 if(node[x].left==node[x].right) 157 return node[x].left; 158 if(node[x*2].mx+buf+node[x].lazy>z) 159 return que_rb(x*2,z,buf+node[x].lazy); 160 else 161 return que_rb(x*2+1,z,buf+node[x].lazy); 162 } 163 long long get_rb(long long x,long long z){ 164 if(x==1) 165 return -1; 166 if(x%2) 167 return get_rb(x/2,z+node[x].lazy); 168 else 169 { 170 if(z+node[x].lazy<node[x^1].mx) 171 return que_rb(x^1,z+node[x].lazy,0); 172 else 173 return get_rb(x/2,z+node[x].lazy); 174 } 175 } 176 int main(){ 177 freopen("ccsworld.in","r",stdin); 178 freopen("ccsworld.out","w",stdout); 179 scanf("%lld%lld",&n,&m); 180 for(int i=1;i<=n;i++) 181 scanf("%lld",&a[i]); 182 build(1,n,1); 183 for(int i=1;i<=m;i++) 184 { 185 long long tt; 186 scanf("%lld",&tt); 187 if(tt==1) 188 { 189 long long x; 190 scanf("%lld",&x); 191 long long y=get_ls(dl[x],node[dl[x]].mx-node[dl[x]].lazy); 192 if(y!=-1) printf("%lld ",get(y,y,1)); 193 else printf("%lld ",y); 194 } 195 if(tt==2) 196 { 197 long long x; 198 scanf("%lld",&x); 199 long long y=get_lb(dl[x],node[dl[x]].mx-node[dl[x]].lazy); 200 if(y!=-1) printf("%lld ",get(y,y,1)); 201 else printf("%lld ",y); 202 } 203 if(tt==3) 204 { 205 long long x; 206 scanf("%lld",&x); 207 long long y=get_rs(dl[x],node[dl[x]].mx-node[dl[x]].lazy); 208 if(y!=-1) printf("%lld ",get(y,y,1)); 209 else printf("%lld ",y); 210 } 211 if(tt==4) 212 { 213 long long x; 214 scanf("%lld",&x); 215 long long y=get_rb(dl[x],node[dl[x]].mx-node[dl[x]].lazy); 216 if(y!=-1) printf("%lld ",get(y,y,1)); 217 else printf("%lld ",y); 218 } 219 if(tt==5) 220 { 221 long long x,y; 222 scanf("%lld%lld",&x,&y); 223 long long xx,yy; 224 xx=get(x,x,1); 225 yy=get(y,y,1); 226 change(x,x,1,yy); 227 change(y,y,1,xx); 228 } 229 if(tt==6) 230 { 231 long long x,y,z; 232 scanf("%lld%lld%lld",&x,&y,&z); 233 add(x,y,1,z); 234 } 235 if(tt==7) 236 { 237 long long x,y,z; 238 scanf("%lld%lld%lld",&x,&y,&z); 239 add(x,y,1,0-z); 240 } 241 } 242 //while(1); 243 return 0; 244 }
因此,我们大可开个数组存下每个叶节点的编号,便于查找,然后就从我们询问的叶节点向上查找,如果当前位置为右子树,那么就看一下他的兄弟的最小值是否比它小,如果不是,那么继续向上爬,如果是,我们就从他的兄弟节点向下爬。如果当前位置是左子树,我们就得接着向上爬了,他的右子树是不满足条件的,因为本题有区间操作,所以lazy数组就成了一个让人头痛的东西,让我们来慢慢分析。
首先我向上爬时路径上的lazy是影响的,因为我们都是从叶节点向上爬,因此叶节点的数值是最晚更新的,因此向上爬的lazy是一定要加上的,那么有人可能回去问了,有可能之前还有区间操作只是没下放呢,这就不必担心了,因为如果他没得到lazy,他的兄弟一定也没得到,两者抵消。
其次,向下爬的lazy也是会影响的,因为我们要的是最靠近x的叶节点,因此我们应采用先右后左的方法,如果右子树的最小值比上面那步传下来的参数小,就搜右子树,左子树就不必管了,而lazy也是需要一直跟着下放,原理见上。
我们最终传回来的值并不是当前节点的值,因为上面的值可能还没传下来,因此我们传的应当是它的位置,再从根节点向下搞也就是单点查询了。
至于其他三个操作,请读者自己思考并实现。
打完代码后提交,全WA,被QTY2001神犇提醒,没开long long。交完就A了,没啥坑点,主要是码力,被教练员吐槽代码能力弱的我都能在一小时之内搞掉,这道题的难度也是没谁了。这或许就是他只有3星的原因吧。