【学习笔记】(26) cdq 分治 与 整体二分
cdq 分治
基本思想
- 我们要解决一系列问题,这些问题一般包含修改和查询操作,可以把这些问题排成一个序列,用一个区间[L,R]表示。
- 分。递归处理左边区间
和右边区间 的问题。 - 治。合并两个子问题,同时考虑到
内的修改对 内的查询产生的影响。即,用左边的子问题帮助解决右边的子问题。
这就是CDQ分治的基本思想。和普通分治不同的地方在于,普通分治在合并两个子问题的过程中,
应用
1.解决和点对有关的问题
P3810 【模板】三维偏序(陌上花开)
先按照a元素从小到大排序,忽略a元素的影响。然后CDQ分治,按照b元素从小到大的顺序进行归并操作。但是这时候没办法像 求逆序对 一样简单地统计 个数 了,c元素如何处理呢?
这时候比较好的方案就是借助权值树状数组。每次从右边的序列中取出三元组(a,b,c)时,对树状数组查询c值小于(a,b,c)的三元组有多少个;每次从左边序列取出三元组(a,b,c)的时候,根据c值在树状数组中进行修改。注意,每次使用完树状数组记得把树状数组归零。
#include<bits/stdc++.h> #define N 200005 using namespace std; int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } int n,k,cnt,nn; int c[N],ans[N]; struct obj{ int a,b,c; int cnt,id,ans; }s1[N],s2[N]; bool cmp1(obj x,obj y){ return x.a==y.a?(x.b==y.b?x.c<y.c:x.b<y.b):x.a<y.a; } bool cmp2(obj x,obj y){ return x.b==y.b?x.c<y.c:x.b<y.b; } void add(int x,int delta){ for(;x<=k;x+=x&-x) c[x]+=delta; } int ask(int x){ int sum=0; for(;x;x-=x&-x) sum+=c[x]; return sum; } void cdq(int l,int r){ if(l==r) return ; int mid=(l+r)>>1; cdq(l,mid); cdq(mid+1,r); sort(s2+l,s2+1+mid,cmp2); sort(s2+mid+1,s2+r+1,cmp2); int i=mid+1,j=l; for(;i<=r;i++){ while(s2[i].b>=s2[j].b&&j<=mid){ add(s2[j].c,s2[j].cnt); j++; } s2[i].ans+=ask(s2[i].c); } for(int i=l;i<j;i++) add(s2[i].c,-s2[i].cnt); } int main(){ n=read(),k=read(); for(int i=1;i<=n;i++){ s1[i].a=read(),s1[i].b=read(),s1[i].c=read(); s1[i].id=i; } sort(s1+1,s1+1+n,cmp1); for(int i=1;i<=n;i++){ cnt++; if(s1[i].a!=s1[i+1].a||s1[i].b!=s1[i+1].b||s1[i].c!=s1[i+1].c){ s2[++nn].a=s1[i].a,s2[nn].b=s1[i].b,s2[nn].c=s1[i].c,s2[nn].id=s1[i].id,s2[nn].cnt=cnt; cnt=0; } } cdq(1,nn); for(int i=1;i<=nn;i++) ans[s2[i].ans+s2[i].cnt-1]+=s2[i].cnt; for(int i=0;i<n;i++) printf("%d\n",ans[i]); return 0; }
2.优化 1D 动态规划转移
1D 动态规划指的是一类特定的 DP 问题,该类题目的特征是 DP 数组是一维的,转移是 O(n) 的。
而分治能把他们的时间复杂度下降到
通常,这些 dp 条件是多维偏序。
流程
说明 值已经被计算好了,直接令- 递归使用
- 处理所有
的转移关系 - 递归使用
P2487 [SDOI2011] 拦截导弹
朴素dp 是
#include<bits/stdc++.h> #define db double #define ll long long using namespace std; const int N = 5e4 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n; struct node{ int x, y, z; ll dp; db f; }a[N]; ll dp1[N], dp2[N], c1[N]; db f1[N], f2[N], c2[N]; bool cmp1(node a, node b){return a.x == b.x ? (a.y == b.y ? a.z < b.z : a.y > b.y) : a.x > b.x;} bool cmp2(node a, node b){return a.x == b.x ? (a.y == b.y ? a.z < b.z : a.y < b.y) : a.x < b.x;} bool cmp3(node a, node b){return a.y > b.y;} bool cmp4(node a, node b){return a.y < b.y;} void modify(int x, ll dp, db f){ for(; x <= n; x += x & -x){ if(c1[x] < dp) c1[x] = dp, c2[x] = f; else if(c1[x] == dp) c2[x] += f; } } ll query1(int x){ ll res = 0; for(; x; x -= x & -x) res = max(res, c1[x]); return res; } db query2(int x, ll dp){ db res = 0; for(; x; x -= x & -x) if(c1[x] == dp) res += c2[x]; return res; } void del(int x){ for(; x <= n; x += x & -x) c1[x] = c2[x] = 0; } bool check(int i, int j, int opt){ if(opt == 1) return a[i].y >= a[j].y; else return a[i].y <= a[j].y; } void merge_sort(int l, int r, int opt){ int mid = (l + r) >> 1, i = l, j = mid + 1; while(i <= mid && j <= r){ if(check(i, j, opt)) modify(a[i].z, a[i].dp, a[i].f), ++i; else{ int val = query1(a[j].z) + 1; if(a[j].dp < val) a[j].dp = val, a[j].f = query2(a[j].z, val - 1); else if(a[j].dp == val) a[j].f += query2(a[j].z, val - 1); ++j; } } while(j <= r){ int val = query1(a[j].z) + 1; if(a[j].dp < val) a[j].dp = val, a[j].f = query2(a[j].z, val - 1); else if(a[j].dp == val) a[j].f += query2(a[j].z, val - 1); ++j; } for(int _ = l; _ < i; ++_) del(a[_].z); } void cdq(int l, int r, int opt){ if(l == r) return ; int mid = (l + r) >> 1; cdq(l, mid, opt); if(opt == 1) sort(a + l, a + mid + 1, cmp3), sort(a + mid + 1, a + r + 1, cmp3); else sort(a + l, a + mid + 1, cmp4), sort(a + mid + 1, a + r + 1, cmp4); merge_sort(l, r, opt); if(opt == 1) sort(a + mid + 1, a + r + 1, cmp1); else sort(a + mid + 1, a + r + 1, cmp2); cdq(mid + 1, r, opt); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; // freopen("3.in", "r", stdin); // freopen("1.out", "w", stdout); n = read(); for(int i = 1; i <= n; ++i){ a[i].x = read(), a[i].y = read(), a[i].z = i; a[i].f = a[i].dp = 1; } sort(a + 1, a + 1 + n, cmp1); cdq(1, n, 1); ll ans = 0; for(int i = 1; i <= n; ++i) dp1[a[i].z] = a[i].dp, f1[a[i].z] = a[i].f, ans = max(ans, dp1[a[i].z]); printf("%lld\n", ans); for(int i = 1; i <= n; ++i) a[i].z = n - a[i].z + 1, a[i].dp = a[i].f = 1; sort(a + 1, a + 1 + n, cmp2); cdq(1, n, 2); db k = 0; for(int i = 1; i <= n; ++i){ dp2[n - a[i].z + 1] = a[i].dp, f2[n - a[i].z + 1] = a[i].f; if(dp2[n - a[i].z + 1] == ans) k += f2[n - a[i].z + 1]; } for(int i = 1; i <= n; ++i){ if(dp1[i] + dp2[i] == ans + 1) printf("%.5lf", f1[i] * f2[i] / k); else printf("0.00000"); if(i < n) printf(" "); } puts(""); return 0; }
3.动态问题转化为静态问题
前两种情况使用分治目的是: 将序列折半,递归处理点对间的关系。
不过现在要求折半 时间序列。
它适用于一些「需要支持做 XXX 修改然后做 XXX 询问」的数据结构题。该类题目有两个特点:
- 把询问离线,所有操作会按时间排成一个序列。
- 每一个修改和之后的询问息相关,有
对。
分治的操作和处理点对关系的分支操作也相同。
如果各个修改之间是 独立 的话,就不需要处理左右区间的时序问题。
如果不独立,那么两个区间可能会进行依赖关系,此时所有跨越
P3206 [HNOI2010] 城市建设
我们可以通过分治,一层层的统一求出一定用到的边和一定用不到的边,达到减少边数的效果,最后到达边界时再进行暴力,可以起到很好的加速效果。本质上就是将单独选边转化为给一个区间的所有询问选边,起到优化时间的效果。
具体的,我们的函数 CDQ(l, r) 就是要删除对于
具体的,我们把 [l,r] 中要修改的边的权值都改为
然后,我们再把
而计算顺序是:先按顺序执行上述计算,再计算 CDQ(l,mid),再计算 CDQ(mid+1,r)。只有这样才能保证计算 CDQ(l,r) 时, 1 到 l−1 的所有边权都修改了过来。而上述两步计算出来的边是在不管 [l,r] 怎么修改边权都成立的,所以没有关系。最后,我们带着计算出来的边,进行下一层递归即可。
#include<bits/stdc++.h> #define db double #define ll long long #define INF 1e9 using namespace std; const int N = 5e4 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n, m, q; int a[N], b[N], c[N], fa[N], ct[N]; ll ans[N]; struct edge{ int u, v, w, id; bool operator < (const edge &A) const{return w < A.w;} }e[20][N], f[N], g[N]; int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);} void clear(int tot){ for(int i = 1; i <= tot; ++i) fa[f[i].u] = f[i].u, fa[f[i].v] = f[i].v; } void cdq(int l, int r, int now, int tot, ll sum){ if(l == r) ct[a[l]] = b[l]; for(int i = 1; i <= tot; ++i) e[now][i].w = ct[e[now][i].id], c[e[now][i].id] = i, f[i] = e[now][i]; clear(tot); if(l == r){ ans[l] = sum, sort(f + 1, f + 1 + tot); for(int i = 1; i <= tot; ++i){ int x = find(f[i].u), y = find(f[i].v); if(x == y) continue; ans[l] += f[i].w, fa[x] = y; } return ; } for(int i = l; i <= r; ++i) f[c[a[i]]].w = -INF; sort(f + 1, f + 1 + tot); int cnt = 0; for(int i = 1; i <= tot; ++i){ int x = find(f[i].u), y = find(f[i].v); if(x == y) continue; fa[x] = y; if(f[i].w != -INF) g[++cnt] = f[i], sum += f[i].w; } clear(tot); for(int i = 1; i <= cnt; ++i) fa[find(g[i].u)] = g[i].v; cnt = 0; for(int i = 1; i <= tot; ++i){ int x = find(f[i].u), y = find(f[i].v); if(x == y) continue; f[++cnt].u = x, f[cnt].v = y, f[cnt].w = f[i].w, f[cnt].id = f[i].id; } tot = cnt; for(int i = 1; i <= tot; ++i) c[f[i].id] = i; for(int i = l; i <= r; ++i) f[c[a[i]]].w = INF; clear(tot); sort(f + 1, f + 1 + tot); cnt = 0; for(int i = 1; i <= tot; ++i){ if(f[i].w == INF){f[++cnt] = f[i]; continue;} int x = find(f[i].u), y = find(f[i].v); if(x == y) continue; fa[x] = y, f[++cnt] = f[i]; } tot = cnt; for(int i = 1; i <= tot; ++i) e[now + 1][i] = f[i]; int mid = (l + r) >> 1; cdq(l, mid, now + 1, tot, sum); cdq(mid + 1, r, now + 1, tot, sum); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(), m = read(), q = read(); for(int i = 1; i <= m; ++i) e[0][i].u = read(), e[0][i].v = read(), e[0][i].w = read(), e[0][i].id = i, ct[i] = e[0][i].w; for(int i = 1; i <= q; ++i) a[i] = read(), b[i] = read(); cdq(1, q, 0, m, 0); for(int i = 1; i <= q; ++i) printf("%lld\n", ans[i]); return 0; }
整体二分
整体二分主要是把所有查询放在一起二分答案, 然后把操作也一起分治.
对没错, 放在一起. 计算的时候扫一遍当前处理的区间, 把答案在 [l,mid] 的查询放在一边递归下去, (mid,r] 的放在另一边递归下去, 递归到叶子就有答案了.
其实有点像是二分在数据结构题目上的扩展, 因为数据结构题一般不仅有多组查询还有其他的修改...
什么时候可以使用呢?
- 询问的答案具有可二分性。
- 修改对判定答案的贡献互相独立,修改之间互不影响效果。
- 修改如果对判定答案有贡献,则贡献为确定的判定标准无关的值。
- 贡献满足交换律,结合律,具有可加性。
记 [l,r] 为答案的值域,[L,R] 为答案的定义域(下标在 [L,R]之间)
- 首先把所有操作按照时间顺序存入数组中,然后开始分治。
- 在每一层分治中,利用数据结构统计当前查询的答案和 mid之间的关系(感觉有点像 CDQ分治)
- 根据查询出来的答案和 mid之间的关系,将当前处理的操作序列分成
和 两份,并且递归。 - 当 l=r,记录答案返回即可。
带修区间第 k 小
P2617 Dynamic Rankings
为了方便,将询问和修改统称为操作。因为后面的操作会依赖于前面的操作,因此可以将所有操作存在一个数组里,使用 标记 区分类型。
修改操作可以理解成:从原数列中删除一个数再添加一个数。
因此,可转化为:树状数组中删除之前的数,再插入现在的数。
数列的初始化操作可简化为插入操作。
首先原序列记为
1、将A序列中值在
2、对于每一个询问
在树状数组中查询
如果当前询问
3、清空树状数组,递归到
当
#include<bits/stdc++.h> using namespace std; const int N = 2e6 + 57, INF = 1e9; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n, m, cnt; int a[N], ans[N], c[N]; struct node{ int x, y, k, id, opt; }q[N], q1[N], q2[N]; void add(int x, int k){ for(; x <= n; x += x & -x) c[x] += k; } int ask(int x){ int res = 0; for(; x; x -= x & -x) res += c[x]; return res; } void solve(int L, int R, int l, int r){ if(l > r || L > R) return ; if(l == r){ for(int i = L; i <= R; ++i) if(q[i].opt == 2) ans[q[i].id] = l; return ; } int cnt1 = 0, cnt2 = 0, mid = (l + r) >> 1; for(int i = L; i <= R; ++i){ if(q[i].opt != 2){ if(q[i].x <= mid) add(q[i].id, q[i].opt), q1[++cnt1] = q[i]; //只需要考虑左边对右边的贡献 else q2[++cnt2] = q[i]; }else{ int res = ask(q[i].y) - ask(q[i].x - 1); if(res >= q[i].k) q1[++cnt1] = q[i]; else q[i].k -= res, q2[++cnt2] = q[i]; } } for(int i = 1; i <= cnt1; ++i) if(q1[i].opt != 2) add(q1[i].id, -q1[i].opt); for(int i = 1; i <= cnt1; ++i) q[i + L - 1] = q1[i]; for(int i = 1; i <= cnt2; ++i) q[i + L + cnt1 - 1] = q2[i]; solve(L, L + cnt1 - 1, l, mid), solve(L + cnt1, R, mid + 1, r); } bool _v; int main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(), m = read(); for(int i = 1; i <= n; ++i) a[i] = read(), q[++cnt] = {a[i], 0, 0, i, 1}; int num = 0; for(int i = 1; i <= m; ++i){ char ch[5]; scanf("%s", ch + 1); int l, r, x, k; if(ch[1] == 'Q'){ l = read(), r = read(), k = read(); q[++cnt] = {l, r, k, ++num, 2}; }else{ x = read(), k = read(); q[++cnt] = {a[x], 0, 0, x, -1}, q[++cnt] = {a[x] = k, 0, 0, x, 1}; } } solve(1, cnt, 0, INF); for(int i = 1; i <= num; ++i) printf("%d\n", ans[i]); return 0; }
P4602 [CTSC2018] 混合果汁
整体二分,将美味度不小于
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 57, INF = 1e18; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n, m, sum; int ans[N], c[N], s[N]; struct node{ int d, p, l; bool operator < (const node &A) const{return d > A.d;} }a[N]; struct query{ int g, L, id; }b[N], q1[N], q2[N]; void add(int x, int k){ int w = x * k; sum += k; for(; x < N; x += x & -x) c[x] += k, s[x] += w; } int ask(int k){ if(k > sum) return INF; int x = 0, res = 0; for(int i = 16; ~i; --i){ int nx = x + (1 << i); if(nx >= N || c[nx] >= k) continue; k -= c[nx]; res += s[nx]; x = nx; } return res + (x + 1) * k; } void solve(int l, int r, int L, int R){ if(l > r || L > R) return ; if(l == r){ if(l > n) return ; for(int i = L; i <= R; ++i) ans[b[i].id] = a[l].d; return ; } int mid = (l + r) >> 1; for(int i = l; i <= mid; ++i) add(a[i].p, a[i].l); int cnt1 = 0, cnt2 = 0; for(int i = L; i <= R; ++i){ int x = ask(b[i].L); if(x <= b[i].g) q1[++cnt1] = b[i]; else q2[++cnt2] = b[i]; } for(int i = 1; i <= cnt1; ++i) b[i + L - 1] = q1[i]; for(int i = 1; i <= cnt2; ++i) b[i + L + cnt1 - 1] = q2[i]; solve(mid + 1, r, L + cnt1, R); for(int i = l; i <= mid; ++i) add(a[i].p, -a[i].l); solve(l, mid, L, L + cnt1 - 1); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(), m = read(); for(int i = 1; i <= n; ++i) a[i].d = read(), a[i].p = read(), a[i].l = read(); sort(a + 1, a + 1 + n); for(int i = 1; i <= m; ++i) b[i].g = read(), b[i].L = read(), b[i].id = i; memset(ans, -1, sizeof(ans)); solve(1, n + 1, 1, m); for(int i = 1; i <= m; ++i) printf("%lld\n", ans[i]); return 0; }
P3527 [POI2011] MET-Meteors
整体二分,就是因为是环,所以有些点要特殊处理。
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 6e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n, m, k; int p[N], ans[N], c[N]; vector<int> own[N]; struct qry{ int l, r, a, opt, id; }q[N], q1[N], q2[N]; void add(int x, int k){ if(!x) return ; for(; x <= m; x += x & -x) c[x] += k; } int ask(int x){ int res = 0; for(; x; x -= x & -x) res += c[x]; return res; } void solve(int L, int R, int l, int r){ if(L > R || l > r) return ; if(l == r){ for(int i = L; i <= R; ++i) if(q[i].opt == 2) ans[q[i].id] = l; return ; } int mid = (l + r) >> 1, cnt1 = 0, cnt2 = 0; for(int i = L; i <= R; ++i){ if(q[i].opt == 2){ int res = 0; for(auto j : own[q[i].id]){ res += ask(j); if(res >= q[i].a) break; } if(q[i].a <= res) q1[++cnt1] = q[i]; else q[i].a -= res, q2[++cnt2] = q[i]; }else{ if(q[i].id <= mid){ if(q[i].opt == 1) add(1, q[i].a), add(q[i].r + 1, -q[i].a), add(q[i].l, q[i].a); else add(q[i].l, q[i].a), add(q[i].r + 1, -q[i].a); q1[++cnt1] = q[i]; }else q2[++cnt2] = q[i]; } } for(int i = L; i <= R; ++i){ if(q[i].opt != 2 && q[i].id <= mid){ if(q[i].opt == 1) add(1, -q[i].a), add(q[i].r + 1, q[i].a), add(q[i].l, -q[i].a); else add(q[i].l, -q[i].a), add(q[i].r + 1, q[i].a); } } for(int i = 1; i <= cnt1; ++i) q[i + L - 1]= q1[i]; for(int i = 1; i <= cnt2; ++i) q[i + L + cnt1 - 1] = q2[i]; solve(L, L + cnt1 - 1, l, mid); solve(L + cnt1, R, mid + 1, r); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(), m = read(); for(int i = 1, x; i <= m; ++i) x = read(), own[x].push_back(i); for(int i = 1; i <= n; ++i) p[i] = read(); k = read(); for(int i = 1; i <= k; ++i){ q[i].l = read(), q[i].r = read(), q[i].a = read(), q[i].id = i; if(q[i].r < q[i].l) q[i].opt = 1; } for(int i = 1; i <= n; ++i) q[i + k].a = p[i], q[i + k].opt = 2, q[i + k].id = i; solve(1, k + n, 1, k + 1); for(int i = 1; i <= n; ++i){ if(ans[i] != k + 1) printf("%d\n", ans[i]); else printf("NIE\n"); } return 0; }
P1527 [国家集训队] 矩阵乘法
维护改为二维树状数组维护前缀和即可。
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 5e2 + 57, M = 6e5 + 67; int read(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -f; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();} return x * f; } bool _u; int n, m, cnt, minn = 1e9, maxn; struct qry{ int a1, b1, a2, b2, k, id; }q[M], q1[M], q2[M]; int c[N][N], ans[M]; void add(int x, int y, int v){ for(int i = x; i <= n; i += i & -i) for(int j = y; j <= n; j += j & -j) c[i][j] += v; } int ask(int x, int y){ int res = 0; for(int i = x; i; i -= i & -i) for(int j = y; j; j -= j & -j) res += c[i][j]; return res; } void solve(int l, int r, int L, int R){ if(l > r || L > R) return ; if(l == r){ for(int i = L; i <= R; ++i) ans[q[i].id] = l; return ; } int mid = (l + r) >> 1; int cnt1 = 0, cnt2 = 0; for(int i = L; i <= R; ++i){ if(q[i].id == 0) { if(q[i].k <= mid) add(q[i].a1, q[i].b1, 1), q1[++cnt1] = q[i]; else q2[++cnt2] = q[i]; }else{ int tmp = ask(q[i].a2, q[i].b2) - ask(q[i].a1 - 1, q[i].b2) - ask(q[i].a2, q[i].b1 - 1) + ask(q[i].a1 - 1, q[i].b1 - 1); if(tmp >= q[i].k) q1[++cnt1] = q[i]; else q[i].k -= tmp, q2[++cnt2] = q[i]; } } for(int i = 1; i <= cnt1; ++i) q[L + i - 1] = q1[i]; for(int i = 1; i <= cnt2; ++i) q[L + cnt1 + i - 1] = q2[i]; for(int i = 1; i <= cnt1; ++i) if(!q1[i].id && q1[i].k <= mid) add(q1[i].a1, q1[i].b1, -1); solve(l, mid, L, L + cnt1 - 1); solve(mid + 1, r, L + cnt1, R); } bool _v; signed main(){ cerr << abs(&_u - &_v) / 1048576.0 << " MB\n"; n = read(), m = read(); for(int i = 1, x; i <= n; ++i) for(int j = 1; j <= n; ++j) q[++cnt] = {i, j, 0, 0, x = read(), 0}, minn = min(minn, x), maxn = max(maxn, x); for(int i = 1, a1, a2, b1, b2, k; i <= m; ++i) q[++cnt] = {a1 = read(), b1 = read(), a2 = read(), b2 = read(), k = read(), i}; solve(0, 1e9, 1, cnt); for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]); return 0; }
本文作者:南风未起
本文链接:https://www.cnblogs.com/jiangchen4122/p/17694555.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步