LOJ#2553 暴力写挂

本文探讨了在两棵树T1、T2中寻找特定路径组合以最大化路径间距离的问题,通过巧妙转换和分治策略,结合虚树构建与线段树优化,实现了高效的算法设计。

题意:给定两棵树T1,T2,求d1[x] + d1[y] - d1[lca1(x, y)] - d2[lca2(x, y)]的最大值。

解:考虑把上面这个毒瘤东西化一下。发现它就是T1中x,y到根的路径并 - T2中x,y到根的路径交。

也就是T1中的一个三叉状路径和T2中lca到根的路径。

根据情报中心的套路,把这个东西 * 2,发现就是d1[x] + d1[y] + dis1(x, y) - d2[x] - d2[y] + dis2(x, y)

令val[i] = d1[i] - d2[i],我们就要令val[x] + val[y] + dis1(x, y) + dis2(x, y)最大。

考虑在T1上边分治,这样的话dis1(x, y) = d[x] + d[y] + e。e是分治边的长度。

在T2上把T1这次分治的点全部提出来建虚树。我们不妨把每个T1的节点挂到对应的虚树节点上,边权为val[x] + d[x],那么我们就是要在虚树上挂的点中找到两个异色节点,使得它们距离最远。

此时我们有一个非常优秀的O(n)DFS算法......但是我就是SB中的SB中的SB,把这个问题搞成了nlogn。这个解决之后本题就做完了。为了卡常用了优秀的zkw线段树。

  1 #include <bits/stdc++.h>
  2 
  3 #define forson(x, i) for(register int i = e[x]; i; i = edge[i].nex)
  4 
  5 #define add(x, y, z) edge[++tp].v = y; edge[tp].len = z; edge[tp].nex = e[x]; e[x] = tp
  6 #define ADD(x, y) EDGE[++TP].v = y; EDGE[TP].nex = E[x]; E[x] = TP
  7 
  8 typedef long long LL;
  9 const int N = 800010;
 10 const LL INF = 4e18;
 11 
 12 inline char gc() {
 13     static char buf[N], *p1(buf), *p2(buf);
 14     if(p1 == p2) p2 = (p1 = buf) + fread(buf, 1, N, stdin);
 15     return p1 == p2 ? EOF : *p1++;
 16 }
 17 
 18 template <class T> inline void read(T &x) {
 19     x = 0;
 20     register char c(gc());
 21     bool f(false);
 22     while(c < '0' || c > '9') {
 23         if(c == '-') f = true;
 24         c = gc();
 25     }
 26     while(c >= '0' && c <= '9') {
 27         x = x * 10 + c - 48;
 28         c = gc();
 29     }
 30     if(f) x = (~x) + 1;
 31     return;
 32 }
 33 
 34 struct Edge {
 35     int nex, v;
 36     bool vis;
 37     LL len;
 38 }edge[N << 1], EDGE[N << 1]; int tp = 1, TP;
 39 
 40 int pw[N << 1], n, e[N], Cnt, _n, small, root, imp[N], K, use[N], Time, stk[N], top, E[N], RT, POS[N], NUM, SIZ[N], ID[N], vis[N], siz[N];
 41 LL val[N], ans = -INF, d[N], VAL[N], H[N], C[N], W[N];
 42 std::vector<int> G[N];
 43 std::vector<LL> Len[N];
 44 
 45 namespace seg2 {
 46     LL large[N << 2];
 47     void build(int l, int r, int o) {
 48         if(l == r) {
 49             large[o] = VAL[ID[r]];
 50             //if(F) printf("p = %d x = %d val = %lld \n", r, ID[r], VAL[ID[r]]);
 51             return;
 52         }
 53         int mid((l + r) >> 1);
 54         build(l, mid, o << 1);
 55         build(mid + 1, r, o << 1 | 1);
 56         large[o] = std::max(large[o << 1], large[o << 1 | 1]);
 57         return;
 58     }
 59     LL getMax(int L, int R, int l, int r, int o) {
 60         if(L <= l && r <= R) {
 61             return large[o];
 62         }
 63         LL ans = -INF;
 64         int mid((l + r) >> 1);
 65         if(L <= mid) ans = getMax(L, R, l, mid, o << 1);
 66         if(mid < R) ans = std::max(ans, getMax(L, R, mid + 1, r, o << 1 | 1));
 67         return ans;
 68     }
 69 }
 70 
 71 namespace seg {
 72     LL large[N << 2];
 73     int lm;
 74     inline void build(int n) {
 75         lm = 1; n += 2;
 76         while(lm < n) lm <<= 1;
 77         large[lm] = -INF;
 78         for(register int i = 1; i <= n; i++) {
 79             large[i + lm] = VAL[ID[i]];
 80         }
 81         for(register int i = n + 1; i < lm; i++) {
 82             large[i + lm] = -INF;
 83         }
 84         for(int i = lm - 1; i >= 1; i--) {
 85             large[i] = std::max(large[i << 1], large[i << 1 | 1]);
 86         }
 87         return;
 88     }
 89     inline LL getMax(int L, int R) {
 90         LL ans = -INF;
 91         for(int s = lm + L - 1, t = lm + R + 1; s ^ t ^ 1; s >>= 1, t >>= 1) {
 92             if(t & 1) {
 93                 ans = std::max(ans, large[t ^ 1]);
 94             }
 95             if((s & 1) == 0) {
 96                 ans = std::max(ans, large[s ^ 1]);
 97             }
 98         }
 99         return ans;
100     }
101 }
102 
103 namespace t1 {
104     int e[N], pos2[N], ST[N << 1][20], num2, deep[N];
105     LL d[N];
106     Edge edge[N << 1]; int tp;
107     void DFS_1(int x, int f) {
108         deep[x] = deep[f] + 1;
109         pos2[x] = ++num2;
110         ST[num2][0] = x;
111         forson(x, i) {
112             int y(edge[i].v);
113             if(y == f) continue;
114             d[y] = d[x] + edge[i].len;
115             DFS_1(y, x);
116             ST[++num2][0] = x;
117         }
118         return;
119     }
120     inline void prework() {
121         for(register int j(1); j <= pw[num2]; j++) {
122             for(register int i(1); i + (1 << j) - 1 <= num2; i++) {
123                 if(deep[ST[i][j - 1]] < deep[ST[i + (1 << (j - 1))][j - 1]]) {
124                     ST[i][j] = ST[i][j - 1];
125                 }
126                 else {
127                     ST[i][j] = ST[i + (1 << (j - 1))][j - 1];
128                 }
129             }
130         }
131         return;
132     }
133     inline int lca(int x, int y) {
134         x = pos2[x], y = pos2[y];
135         if(x > y) std::swap(x, y);
136         int t(pw[y - x + 1]);
137         if(deep[ST[x][t]] < deep[ST[y - (1 << t) + 1][t]]) {
138             return ST[x][t];
139         }
140         else {
141             return ST[y - (1 << t) + 1][t];
142         }
143     }
144     inline LL dis(int x, int y) {
145         return d[x] + d[y] - 2 * d[lca(x, y)];
146     }
147 }
148 
149 void rebuild(int x, int f) {
150     int temp(0);
151     for(register int i(0); i < G[x].size(); i++) {
152         int y = G[x][i];
153         LL len = Len[x][i];
154         if(y == f) continue;
155         if(!temp) {
156             add(x, y, len);
157             add(y, x, len);
158             temp = x;
159         }
160         else if(i == G[x].size() - 1) {
161             add(temp, y, len);
162             add(y, temp, len);
163         }
164         else {
165             add(temp, ++Cnt, 0);
166             add(Cnt, temp, 0);
167             temp = Cnt;
168             add(temp, y, len);
169             add(y, temp, len);
170         }
171         d[y] = d[x] + len;
172         rebuild(y, x);
173     }
174     return;
175 }
176 
177 void getroot(int x, int f) {
178     siz[x] = 1;
179     //printf("getroot : x = %d \n", x);
180     forson(x, i) {
181         int y(edge[i].v);
182         if(y == f || edge[i].vis) continue;
183         getroot(y, x);
184         if(small > std::max(siz[y], _n - siz[y])) {
185             small = std::max(siz[y], _n - siz[y]);
186             root = i;
187         }
188         siz[x] += siz[y];
189     }
190     //printf("siz %d = %d \n", x, siz[x]);
191     return;
192 }
193 
194 void DFS_1(int x, int f, int flag) {
195     siz[x] = 1;
196     //printf("x = %d d = %lld \n", x, d[x]);
197     if(!flag && x <= n) {
198         imp[++K] = x;
199         vis[x] = Time;
200         //if(F) printf("vis %d = Time \n", x);
201     }
202     else if(flag && x <= n) {
203         imp[++K] = x;
204     }
205     forson(x, i) {
206         int y(edge[i].v);
207         if(y == f || edge[i].vis) continue;
208         d[y] = d[x] + edge[i].len;
209         DFS_1(y, x, flag);
210         siz[x] += siz[y];
211     }
212     return;
213 }
214 
215 inline void work(int x) {
216     if(use[x] == Time) return;
217     use[x] = Time;
218     E[x] = 0;
219     return;
220 }
221 
222 inline bool cmp(const int &a, const int &b) {
223     return t1::pos2[a] < t1::pos2[b];
224 }
225 
226 inline void build_t() {
227     std::sort(imp + 1, imp + K + 1, cmp);
228     K = std::unique(imp + 1, imp + K + 1) - imp - 1;
229     work(imp[1]);
230     stk[top = 1] = imp[1];
231     for(register int i(2); i <= K; i++) {
232         int x = imp[i], y = t1::lca(x, stk[top]);
233         work(x); work(y);
234         while(top > 1 && t1::pos2[y] <= t1::pos2[stk[top - 1]]) {
235             ADD(stk[top - 1], stk[top]);
236             top--;
237         }
238         if(y != stk[top]) {
239             ADD(y, stk[top]);
240             stk[top] = y;
241         }
242         stk[++top] = x;
243     }
244     while(top > 1) {
245         ADD(stk[top - 1], stk[top]);
246         top--;
247     }
248     RT = stk[top];
249     return;
250 }
251 
252 void dfs_2(int x) {
253     //printf("dfs_2 x = %d \n", x);
254     SIZ[x] = 1;
255     POS[x] = ++NUM;
256     ID[NUM] = x;
257     if(vis[x] == Time) VAL[x] = t1::d[x] + val[x] + d[x];
258     else VAL[x] = -INF;
259     /*if(F && x == 3) {
260         printf("VAL 3 = %lld + %lld + %lld \n", t1.d[x], val[x], d[x]);
261     }*/
262     C[x] = VAL[x];
263     for(register int i(E[x]); i; i = EDGE[i].nex) {
264         int y = EDGE[i].v;
265         dfs_2(y);
266         SIZ[x] += SIZ[y];
267         C[x] = std::max(C[x], C[y]);
268     }
269     //if(F) printf("x = %d VAL = %lld  C = %lld \n", x, VAL[x], C[x]);
270     return;
271 }
272 
273 void dfs_3(int x, int f) {
274     if(f) {
275         /// cal H[x]
276         H[x] = seg::getMax(POS[f], POS[x] - 1);
277         if(POS[x] + SIZ[x] < POS[f] + SIZ[f]) {
278             H[x] = std::max(H[x], seg::getMax(POS[x] + SIZ[x], POS[f] + SIZ[f] - 1));
279         }
280         W[x] = std::max(W[f], H[x] - 2 * t1::d[f]);
281     }
282     else {
283         H[x] = W[x] = -INF;
284     }
285     //if(F) printf("x = %d H = %lld W = %lld \n", x, H[x], W[x]);
286     for(register int i(E[x]); i; i = EDGE[i].nex) {
287         int y(EDGE[i].v);
288         dfs_3(y, x);
289     }
290     return;
291 }
292 
293 void DFS_4(int x, int f, LL v) {
294     /// ask
295     if(x <= n) {
296         VAL[x] = t1::d[x] + val[x] + d[x] + v;
297         ans = std::max(ans, VAL[x] + W[x]);
298         ans = std::max(ans, VAL[x] + C[x] - 2 * t1::d[x]);
299     }
300     forson(x, i) {
301         int y(edge[i].v);
302         if(y == f || edge[i].vis) continue;
303         DFS_4(y, x, v);
304     }
305     return;
306 }
307 
308 void e_div(int x) {
309     if(_n == 1) {
310         ans = std::max(ans, val[x] << 1);
311         return;
312     }
313     small = N;
314     getroot(x, 0);
315     edge[root].vis = edge[root ^ 1].vis = 1;
316 
317     x = edge[root].v;
318     int y = edge[root ^ 1].v;
319     if(siz[x] < _n - siz[x]) std::swap(x, y);
320 
321     //if(F) printf("div %d %d  _n = %d \n", x, y, _n);
322 
323     ///
324     d[x] = d[y] = 0;
325     TP = K = 0;
326     Time++;
327     DFS_1(x, 0, 0);
328     DFS_1(y, 0, 1);
329     /// build virtue trEE
330     build_t();
331     //printf("v_t build over \n");
332     /// prework
333     NUM = 0;
334     dfs_2(RT);
335     //printf("dfs_2 over \n");
336     seg::build(NUM);
337     //printf("seg build over \n");
338     dfs_3(RT, 0);
339     DFS_4(y, 0, edge[root].len);
340 
341     //if(F) printf("ans = %d \n", ans);
342 
343     _n = siz[x];
344     e_div(x);
345     _n = siz[y];
346     e_div(y);
347     return;
348 }
349 
350 int main() {
351 
352     W[0] = -INF;
353     read(n);
354     LL z;
355     for(register int i(1), x, y; i < n; i++) {
356         read(x); read(y); read(z);
357         G[x].push_back(y);
358         G[y].push_back(x);
359         Len[x].push_back(z);
360         Len[y].push_back(z);
361     }
362     for(register int i(1), x, y; i < n; i++) {
363         read(x); read(y); read(z);
364         t1::edge[++t1::tp].v = y;
365         t1::edge[t1::tp].len = z;
366         t1::edge[t1::tp].nex = t1::e[x];
367         t1::e[x] = t1::tp;
368         t1::edge[++t1::tp].v = x;
369         t1::edge[t1::tp].len = z;
370         t1::edge[t1::tp].nex = t1::e[y];
371         t1::e[y] = t1::tp;
372     }
373     for(register int i = 2; i <= n * 2; i++) {
374         pw[i] = pw[i >> 1] + 1;
375     }
376     t1::DFS_1(1, 0);
377     t1::prework();
378 
379     Cnt = n;
380     rebuild(1, 0);
381     for(register int i(1); i <= n; i++) {
382         val[i] = d[i] - t1::d[i];
383     }
384     /// ans = val[i] + val[j] + t0.dis(i, j) + t1.dis(i, j);
385     _n = Cnt;
386     e_div(1);
387 
388     printf("%lld\n", ans >> 1);
389     return 0;
390 }
AC代码

讲一下我的做法。考虑到两个点的距离是d + d - 2 * lca,我们预处理某一颜色的节点,然后枚举另一个颜色的所有点。

由于开店的启发,我们可以把每个点往上更新链,然后每个点往上查询链。

进而发现,查询全是静态的,所以可以预处理出一个点到根路径上的max,变成单点查询。

设VAl[x]为挂上的节点x的深度。C[x]为x的子树中的最大VAL值。

设H[x]为(subtree(fa[x]) \ subtree(x)的最大VAL值) - 2 * VAL[x],W[x]为x到根路径上的最大H值。

那么对于一个点x,它只要和W[x],C[x] - 2 * VAL[x]拼起来就能得到跟它相关的最远点对距离了。

复杂度在于求H[x]的时候使用DFS序 + RMQ,写了一个log。所以总复杂度是nlog2n。

 


有个T1是链且v > 0的部分分。这里我们发现T1的路径并变成了max(d1[x], d1[y]),于是考虑枚举T2中每个点作为lca,要求两个点在其子树中,且max(d1[x], d1[y])最大。显然维护一个子树最大值就好了。实测有80分呢......

还有个迭代乱搞是随机选一个起点,每次迭代找到一个点与之贡献最大并更新那个点为新的起点。多来几次。这样也有80分呢......

比写正解不知道高到哪里去了...

转载于:https://www.cnblogs.com/huyufeifei/p/10714846.html

内容概要:本文系统介绍了算术优化算法(AOA)的基本原理、核心思想及Python实现方法,并通过图像分割的实际案例展示了其应用价值。AOA是一种基于种群的元启发式算法,其核心思想来源于四则运算,利用乘除运算进行全局勘探,加减运算进行局部开发,通过数学优化器加速函数(MOA)和数学优化概率(MOP)动态控制搜索过程,在全局探索与局部开发之间实现平衡。文章详细解析了算法的初始化、勘探与开发阶段的更新策略,并提供了完整的Python代码实现,结合Rastrigin函数进行测试验证。进一步地,以Flask框架搭建前后端分离系统,将AOA应用于图像分割任务,展示了其在实际工程中的可行性与高效性。最后,通过收敛速度、寻优精度等指标评估算法性能,并提出自适应参数调整、模型优化和并行计算等改进策略。; 适合人群:具备一定Python编程基础和优化算法基础知识的高校学生、科研人员及工程技术人员,尤其适合从事人工智能、图像处理、智能优化等领域的从业者;; 使用场景及目标:①理解元启发式算法的设计思想与实现机制;②掌握AOA在函数优化、图像分割等实际问题中的建模与求解方法;③学习如何将优化算法集成到Web系统中实现工程化应用;④为算法性能评估与改进提供实践参考; 阅读建议:建议读者结合代码逐行调试,深入理解算法流程中MOA与MOP的作用机制,尝试在不同测试函数上运行算法以观察性能差异,并可进一步扩展图像分割模块,引入更复杂的预处理或后处理技术以提升分割效果。
06-21
<think>我们正在查询与LOJ6279相关的编程题目或解决方案。LOJ(LibreOJ)是一个在线的评测系统,主要收录算法竞赛题目。根据题号6279,我们需要确定该题目的具体内容。由于我无法直接访问网络,我将基于已知信息进行推理。在算法竞赛中,LOJ的题目编号通常与特定的题目对应。题号6279可能对应一个特定的问题。根据常见的题目类型,我们可以推测它可能涉及数据结构或算法。然而,为了准确回答,我们可以尝试回忆或推断常见的LOJ题目。例如,LOJ6000系列题目多为数据结构题,如线段树、树状数组等。实际上,通过搜索记忆,我回想起LOJ6279是“数列分块入门3”(BlockArray3)题目。这是“分块”系列题目中的一题。题目描述:给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(即最大的小于x的数)。因此,用户需要的是关于LOJ6279题目的描述和解决方案。解决方案:由于题目要求分块处理,我们将数列分成若干块,每块大小为sqrt(n)。对于每个块,我们维护一个有序的数组(用于二分查找)以及一个加法标记(记录整个块被加了多少)。操作:1.区间加法:对于整块,直接更新加法标记;对于不完整的块,暴力更新元素值,然后重新排序该块(或者重新构建有序数组)。2.查询区间内小于x的前驱:遍历区间内的所有块。对于整块,在块的有序数组中二分查找小于(x-加法标记)的最大值;对于不完整的块,暴力遍历每个元素,判断是否小于x(注意加上加法标记)并更新最大值。代码实现(C++)示例:```cpp#include<iostream>#include<vector>#include<cmath>#include<algorithm>#include<climits>usingnamespacestd;constintMAXN=100010;constintBLOCK_SIZE=320;//约sqrt(MAXN)intn;vector<int>a;vector<int>block_add;//每块的加法标记vector<vector<int>>sorted_blocks;//每块的有序数组voidinit(){intblock_num=(n+BLOCK_SIZE-1)/BLOCK_SIZE;block_add.assign(block_num,0);sorted_blocks.resize(block_num);for(inti=0;i<n;i++){sorted_blocks[i/BLOCK_SIZE].push_back(a[i]);}for(inti=0;i<block_num;i++){sort(sorted_blocks[i].begin(),sorted_blocks[i].end());}}voidupdate_block(intblock_id){sorted_blocks[block_id].clear();intstart=block_id*BLOCK_SIZE;intend=min((block_id+1)*BLOCK_SIZE,n);for(inti=start;i<end;i++){sorted_blocks[block_id].push_back(a[i]);}sort(sorted_blocks[block_id].begin(),sorted_blocks[block_id].end());}voidadd(intl,intr,intc){intblock_l=l/BLOCK_SIZE,block_r=r/BLOCK_SIZE;if(block_l==block_r){for(inti=l;i<=r;i++){a[i]+=c;}update_block(block_l);}else{for(inti=l;i<(block_l+1)*BLOCK_SIZE;i++){a[i]+=c;}update_block(block_l);for(inti=block_l+1;i<block_r;i++){block_add[i]+=c;}for(inti=block_r*BLOCK_SIZE;i<=r;i++){a[i]+=c;}update_block(block_r);}}intquery(intl,intr,intc){intblock_l=l/BLOCK_SIZE,block_r=r/BLOCK_SIZE;intans=-1;if(block_l==block_r){for(inti=l;i<=r;i++){intval=a[i]+block_add[block_l];if(val<c){if(val>ans)ans=val;}}}else{for(inti=l;i<(block_l+1)*BLOCK_SIZE;i++){intval=a[i]+block_add[block_l];if(val<c){if(val>ans)ans=val;}}for(inti=block_l+1;i<block_r;i++){//在块i的有序数组中二分查找小于c-block_add[i]的最大值inttarget=c-block_add[i];autoit=lower_bound(sorted_blocks[i].begin(),sorted_blocks[i].end(),target);if(it!=sorted_blocks[i].begin()){it--;intval=*it+block_add[i];if(val>ans)ans=val;}}for(inti=block_r*BLOCK_SIZE;i<=r;i++){intval=a[i]+block_add[block_r];if(val<c){if(val>ans)ans=val;}}}returnans;}intmain(){cin>>n;a.resize(n);for(inti=0;i<n;i++){cin>>a[i];}init();for(inti=0;i<n;i++){intop,l,r,c;cin>>op>>l>>r>>c;l--;r--;//如果题目输入是1-indexed,则转换为0-indexedif(op==0){add(l,r,c);}else{cout<<query(l,r,c)<<endl;}}return0;}```注意:上述代码是示例代码,可能需要根据题目具体输入要求进行调整。§§相关问题§§1.LOJ6279题目的具体描述是什么?2.分块算法的时间复杂度是多少?3.除了分块,还有哪些方法可以解决LOJ6279?4.如何优化分块算法的常数?5.分块算法适用于哪些场景?</think>### LOJ 6279 题目分析与解决方案 **题目描述**:LOJ 6279 是 LibreOJ 上的 **"数列分块入门 3"**,属于分块算法的经典练习题。核心要求如下: - **操作 1**:区间 $[l, r]$ 内每个数加上值 $c$ - **操作 2**:查询区间 $[l, r]$ 内小于 $c$ 的**最大元素值**(即前驱),不存在时返回 $-1$ #### 分块算法设计 分块的核心思想是将数列划分为 $\sqrt{n}$ 大小的块,平衡暴力操作与批量处理。 ##### 关键数据结构 1. **块内有序数组**: - 每块维护一个排序后的数组,用于二分查找前驱。 - 示例:块 $B_i$ 的排序数组为 $S_i$。 2. **加法懒标记**: - 记录整块被加的总值 $add_i$(不影响块内有序性)。 ##### 操作实现 1. **区间加法($[l, r] + c$)**: - **整块**:直接更新懒标记 $add_i \gets add_i + c$,时间复杂度 $O(1)$。 - **碎块**:暴力修改元素值,并**重构有序数组**(排序),时间复杂度 $O(\sqrt{n} \log \sqrt{n})$。 2. **查询前驱($[l, r]$ 内 $< c$ 的最大值)**: - **整块**:在有序数组 $S_i$ 中二分查找 $c - add_i$ 的前驱,时间复杂度 $O(\log \sqrt{n})$。 - **碎块**:暴力遍历元素,检查 $a_j + add_i < c$ 并更新最大值,时间复杂度 $O(\sqrt{n})$。 #### 代码实现(C++) ```cpp #include <iostream> #include <vector> #include <cmath> #include <algorithm> #include <climits> using namespace std; const int MAXN = 100010; int n, block_size; vector<int> a; vector<int> add_tag; // 块的加法标记 vector<vector<int>> blocks; // 每块的有序数组 void init() { block_size = sqrt(n); int block_num = (n + block_size - 1) / block_size; add_tag.assign(block_num, 0); blocks.resize(block_num); for (int i = 0; i < n; i++) blocks[i / block_size].push_back(a[i]); for (int i = 0; i < block_num; i++) sort(blocks[i].begin(), blocks[i].end()); } void rebuild_block(int block_id) { blocks[block_id].clear(); int start = block_id * block_size; int end = min(start + block_size, n); for (int i = start; i < end; i++) blocks[block_id].push_back(a[i]); sort(blocks[block_id].begin(), blocks[block_id].end()); } void range_add(int l, int r, int c) { int block_l = l / block_size, block_r = r / block_size; if (block_l == block_r) { // 同一块内 for (int i = l; i <= r; i++) a[i] += c; rebuild_block(block_l); } else { // 左侧碎块 for (int i = l; i < (block_l + 1) * block_size; i++) a[i] += c; rebuild_block(block_l); // 中间整块 for (int i = block_l + 1; i < block_r; i++) add_tag[i] += c; // 右侧碎块 for (int i = block_r * block_size; i <= r; i++) a[i] += c; rebuild_block(block_r); } } int query_predecessor(int l, int r, int c) { int ans = -1; int block_l = l / block_size, block_r = r / block_size; // 左侧碎块暴力查询 for (int i = l; i < min(r + 1, (block_l + 1) * block_size); i++) { int val = a[i] + add_tag[block_l]; if (val < c && val > ans) ans = val; } // 中间整块二分查找 for (int i = block_l + 1; i < block_r; i++) { int target = c - add_tag[i]; auto it = lower_bound(blocks[i].begin(), blocks[i].end(), target); if (it != blocks[i].begin()) { it--; int val = *it + add_tag[i]; if (val < c && val > ans) ans = val; } } // 右侧碎块暴力查询 if (block_l != block_r) { for (int i = block_r * block_size; i <= r; i++) { int val = a[i] + add_tag[block_r]; if (val < c && val > ans) ans = val; } } return ans; } int main() { cin >> n; a.resize(n); for (int i = 0; i < n; i++) cin >> a[i]; init(); for (int i = 0; i < n; i++) { int op, l, r, c; cin >> op >> l >> r >> c; l--; r--; // 转为0-indexed if (op == 0) range_add(l, r, c); else cout << query_predecessor(l, r, c) << endl; } return 0; } ``` #### 算法分析 - **时间复杂度**: - 单次修改/查询:$O(\sqrt{n} \log \sqrt{n})$(碎块排序主导)。 - 总操作 $m$ 次:$O(m \sqrt{n} \log n)$。 - **空间复杂度**:$O(n)$。 #### 优化技巧 1. **减少排序次数**: - 碎块修改时只重构受影响块的有序数组。 2. **块大小调整**: - 实测调整块大小为 $n^{0.6}$ 可能更快(需测试)。 #### 应用场景 分块算法适用于**强制在线**的区间问题(如 LOJ 的数列分块系列题),在 $O(\sqrt{n})$ 复杂度下平衡修改与查询[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值