【学习笔记】整体二分
文章目录
- 引
- 整体二分
- 几道模板题
- Dynamic Rankings
- [ZJOI2013]K大数查询
- [国家集训队]矩阵乘法
- [THUPC2017] 天天爱射击
- [CTSC2018]混合果汁
引
例1. 给定 nnn 个数 aia_iai,一次询问,询问区间 [l,r][l,r][l,r] 中的第 kkk 小数。
我们通常想到二分答案,然后计数在 [l,r][l,r][l,r] 区间内 ≤x\le x≤x 的数的个数,再与 kkk 比较。
时间复杂度 O(nlogn)O(n\log n)O(nlogn)。(尽管这并不是最优秀的做法)
例2. 给定 nnn 个数 aia_iai,多次询问,询问区间 [1,n][1,n][1,n] 中的第 kkk 小数。
由于区间是整个数列,所以我们直接排序一下,然后找第 kkk 个数即可,时间复杂度 O(nlogn)O(n\log n)O(nlogn)。
例3. 给定 nnn 个数 aia_iai,多次询问,询问区间 [l,r][l,r][l,r] 中的第 kkk 小数。
这是一道主席树,即可持久化线段树的模板题。建立权值线段树然后 rrr 版本 −l-l−l 版本,线段树上二分。
实际上,我们仍然可以用二分做。
但如果对于每一个询问都二分一次,那么时间复杂度 O(qnlogn)O(qn\log n)O(qnlogn) 就爆炸了。
是否可以只用一次二分呢?——答案是显然的。
我们理想的一次二分应该是二分一个答案 xxx,然后利用一个数据结构快速判断答案和每一个询问的大小关系,再将询问分成 ≤x,>x\le x,>x≤x,>x 的两部分,分别二分下去。
不难发现,这种假想的时间复杂度大概是带两个 logloglog 的。
例4. 给定 nnn 个数 aia_iai,多次询问和修改某个位置的值,询问区间 [l,r][l,r][l,r] 中的第 kkk 小数。
线段树分治??不清楚。但这仍可以二分做。
整体二分
“引”中最后假想出来的思路就是 整体二分 。
能用整体二分解决的题目需要满足以下性质:
- 答案具有可二分性。
- 修改对判断答案的贡献相互独立,即修改之间互不影响。
- 修改如果有贡献,则是确定的贡献,不会随二分的答案产生变化。
- 贡献满足交换律,结合律,具有广义的可加性。
- 离线。
总体算法思路:
- 记 [l,r][l,r][l,r] 为二分答案的值域,[ql,qr][ql,qr][ql,qr] 为定义域,即只考虑编号在 [ql,qr][ql,qr][ql,qr] 中的修改和询问操作,这其中的询问的答案在 [l,r][l,r][l,r] 内。
- 首先把所有操作按 时间顺序 存入数组。通常输入顺序即为操作的时间顺序。然后开始分治。
- 在每一层的分治中,利用数据结构(常见为树状数组/线段树)统计当前查询的答案和 mid\text{mid}mid 的关系。
- 根据查询的答案和 mid\text{mid}mid 之间的关系,分成 L,RL,RL,R 两份,分别递归。
- 类比线段树分治,每一个分治的子问题都是互不相关的,但却共用同一个数据结构。所以这里还要涉及到回退问题。
- 当 l=rl=rl=r 时,[ql,qr][ql,qr][ql,qr] 中的所有询问答案即为 lll。
下面以主席树的模板题作为例题,具体阐释算法实现以及时间复杂度的计算。
将输入 aia_iai 的操作定义为类型 111,即 q[i].op=1q[i].op=1q[i].op=1。并记录下 q[i]q[i]q[i] 修改的位置 iii,修改的值 aia_iai。
将查询 [l,r],k[l,r],k[l,r],k 的操作定义为类型 222。并记录下询问的区间,kkk 。
对于一个形如 (l,r,ql,qr)(l,r,ql,qr)(l,r,ql,qr) 的子问题,二分答案为 mid=l+r>>1mid=l+r>>1mid=l+r>>1。
然后处理 [ql,qr][ql,qr][ql,qr] 范围内的类型 111 操作。
修改的值 ≤mid\le mid≤mid 的。
全都扔到数据结构上。(这里使用的是树状数组)
则树状数组上放的是 ≤mid\le mid≤mid 的且在 [ql,qr][ql,qr][ql,qr] 操作区间内的修改操作。
再放入 LLL 数组中。
修改的值 >mid>mid>mid 的。
这种修改对于 midmidmid 答案没有贡献,直接放进 RRR 数组中。
对于 [ql,qr][ql,qr][ql,qr] 范围中的类型 222 操作。直接询问树状数组中 [q[i].l,q[i].r][q[i].l,q[i].r][q[i].l,q[i].r] 区间内的个数 cntcntcnt。
如果 cnt≤q[i].kcnt\le q[i].kcnt≤q[i].k。
也就是说 [q[i].l,q[i].r][q[i].l,q[i].r][q[i].l,q[i].r] 中 ≤mid\le mid≤mid 的数 ≤k\le k≤k 个。答案要么是 midmidmid 要么 <mid<mid<mid。
根据二分原则,我们把这个询问放到 LLL 数组中。
如果 cnt>q[i].kcnt>q[i].kcnt>q[i].k。
类比线段树二分,我们将 q[i].kq[i].kq[i].k 减去 cntcntcnt。然后放到 RRR 数组中。
因为到时候处理 (mid+1,r,ql′,qr′)(mid+1,r,ql',qr')(mid+1,r,ql′,qr′) 的子问题时,原本 [q[i].l,q[i].r][q[i].l,q[i].r][q[i].l,q[i].r] 中 ≤mid\le mid≤mid 的数时一定会提供 111 的个数,我们没有必要每次都加入。
将数据结构回退至当前层未操作前的模样。
此时的 LLL 数组放的询问是真正 ans≤midans\le midans≤mid 的询问,放的修改是重新计算时还可能被挂到树状数组上去的修改。
RRR 数组同理。我们已经去掉了 LLL 数组中所有修改对 RRR 中询问产生的贡献。
现在两个数组之间的信息是完全不会有交的了。
所以分治处理 (l,mid,L),(mid+1,r,R)(l,mid,L),(mid+1,r,R)(l,mid,L),(mid+1,r,R)。
#include <bits/stdc++.h>
using namespace std;
#define maxn 400005
#define inf 0x7f7f7f7f
int n, m;
int ans[maxn];
struct node { int op, l, r, k, id; }q[maxn], Left[maxn], Right[maxn];namespace BIT {int t[maxn];void add( int i, int x ) { for( ;i <= n;i += i & -i ) t[i] += x; }int ask( int i ) { int cnt = 0; for( ;i;i -= i & -i ) cnt += t[i]; return cnt; }
}void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) if( q[i].op == 2 ) ans[q[i].id] = l;return;}int mid = l + r >> 1, cntL = 0, cntR = 0;for( int i = ql;i <= qr;i ++ )if( q[i].op == 1 ) {if( q[i].k > mid ) Right[++ cntR] = q[i];else BIT :: add( q[i].id, 1 ), Left[++ cntL] = q[i];}else {int cnt = BIT :: ask( q[i].r ) - BIT :: ask( q[i].l - 1 );if( q[i].k <= cnt ) Left[++ cntL] = q[i];else q[i].k -= cnt, Right[++ cntR] = q[i];}for( int i = 1;i <= cntL;i ++ ) if( Left[i].op == 1 ) BIT :: add( Left[i].id, -1 );for( int i = 1;i <= cntL;i ++ ) q[i + ql - 1] = Left[i];for( int i = 1;i <= cntR;i ++ ) q[i + ql + cntL - 1] = Right[i];solve( l, mid, ql, ql + cntL - 1 );solve( mid + 1, r, ql + cntL, qr );
}int main() {scanf( "%d %d", &n, &m );for( int i = 1, x;i <= n;i ++ ) {scanf( "%d", &x );q[i] = (node){ 1, -inf, inf, x, i };}for( int i = 1, l, r, k;i <= m;i ++ ) {scanf( "%d %d %d", &l, &r, &k );q[i + n] = (node){ 2, l, r, k, i };}solve( -inf, inf, 1, n + m );for( int i = 1;i <= m;i ++ ) printf( "%d\n", ans[i] );return 0;
}
最后来分析一下这个时间复杂度:
- 首先是二分的答案值,O(logV)O(\log V)O(logV) 的。
- 对于同一层的分治,总共的区间定义域是 O(n)O(n)O(n)。类比线段树 1,2,41,2,41,2,4 的划分,但同一层的总和仍是 nnn。
- 每个数在同一层的若干个分治中最多只会被挂在数据结构上一次,然后撤销。
- 时间复杂度应为 O(nlognlogV)O(n\log n\log V)O(nlognlogV)。
几道模板题
Dynamic Rankings
Dynamic Rankings
本题才是真的修改操作,即“引”中的例4 。
其实也没有什么特别的,可以类比线段树分治,一个数存在时间是一段连续的区间。
具体见代码即可明白。
#include <bits/stdc++.h>
using namespace std;
#define maxn 400005
struct node { int op, x, y, k, id; }q[maxn], L[maxn], R[maxn];
int n, m;
int ans[maxn], a[maxn];namespace BIT {int t[maxn];void add( int i, int x ) { for( ;i <= n;i += i & -i ) t[i] += x; }int ask( int i ) { int cnt = 0; for( ;i;i -= i & -i ) cnt += t[i]; return cnt; }
}void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) if( q[i].op == 2 ) ans[q[i].id] = l; return; }int mid = l + r >> 1;int cntl = 0, cntr = 0;for( int i = ql;i <= qr;i ++ )if( q[i].op == 2 ) {int cnt = BIT :: ask( q[i].y ) - BIT :: ask( q[i].x - 1 );if( q[i].k <= cnt ) L[++ cntl] = q[i];else q[i].k -= cnt, R[++ cntr] = q[i];}else {if( q[i].x <= mid ) BIT :: add( q[i].k, q[i].y ), L[++ cntl] = q[i];else R[++ cntr] = q[i];}for( int i = ql;i <= qr;i ++ )if( q[i].op == 1 and q[i].x <= mid )BIT :: add( q[i].k, -q[i].y );for( int i = 1;i <= cntl;i ++ ) q[i + ql - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[i + ql + cntl - 1] = R[i];solve( l, mid, ql, ql + cntl - 1 );solve( mid + 1, r, ql + cntl, qr );
}int main() {int cnt = 0, Max = 0;scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );q[++ cnt] = (node){ 1, a[i], 1, i, cnt };Max = max( Max, a[i] );}char op[10]; int x, y, k;for( int i = 1;i <= m;i ++ ) {scanf( "%s", op );if( op[0] == 'Q' ) {scanf( "%d %d %d", &x, &y, &k );q[++ cnt] = (node) { 2, x, y, k, cnt };}else {scanf( "%d %d", &x, &y );q[++ cnt] = (node){ 1, a[x], -1, x, cnt };a[x] = y;q[++ cnt] = (node){ 1, a[x], 1, x, cnt };Max = max( Max, y );}}solve( 0, Max, 1, cnt );sort( q + 1, q + cnt + 1, []( node x, node y ) { return x.id < y.id; } );for( int i = 1;i <= cnt;i ++ )if( q[i].op == 2 ) printf( "%d\n", ans[q[i].id] );return 0;
}
[ZJOI2013]K大数查询
[ZJOI2013]K大数查询
注意这里是 kkk 大数,不是 kkk 小数。
那么二分答案时候,把 >mid>mid>mid 的挂上去即可。
或者你整体取相反数后做 kkk 小数,最后输出答案再取反回来也行。
#include <bits/stdc++.h>
using namespace std;
#define maxn 50005
#define int long long
struct node { int op, x, y, k, id; }q[maxn], L[maxn], R[maxn];
int n, m;
int ans[maxn];namespace SGT {struct node { int cnt, tag; }t[maxn << 2];#define lson now << 1#define rson now << 1 | 1#define mid (l + r >> 1)void pushdown( int now, int l, int r ) {if( ! t[now].tag ) return;t[lson].tag += t[now].tag;t[lson].cnt += t[now].tag * (mid - l + 1);t[rson].tag += t[now].tag;t[rson].cnt += t[now].tag * (r - mid);t[now].tag = 0;}void modify( int now, int l, int r, int L, int R, int x ) {if( R < l or r < L ) return;if( L <= l and r <= R ) { t[now].tag += x; t[now].cnt += x * (r - l + 1); return; }pushdown( now, l, r );modify( lson, l, mid, L, R, x );modify( rson, mid + 1, r, L, R, x );t[now].cnt = t[lson].cnt + t[rson].cnt;}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return t[now].cnt;pushdown( now, l, r );return query( lson, l, mid, L, R ) + query( rson, mid + 1, r, L, R );}#undef lson#undef rson#undef mid
}void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) ans[q[i].id] = l; return; }int mid = l + r >> 1;int cntl = 0, cntr = 0;for( int i = ql;i <= qr;i ++ )if( q[i].op == 1 ) {if( q[i].k > mid ) SGT :: modify( 1, 1, n, q[i].x, q[i].y, 1 ), R[++ cntr] = q[i];else L[++ cntl] = q[i];}else {int cnt = SGT :: query( 1, 1, n, q[i].x, q[i].y );if( q[i].k <= cnt ) R[++ cntr] = q[i];else q[i].k -= cnt, L[++ cntl] = q[i];}for( int i = 1;i <= cntr;i ++ )if( R[i].op == 1 )SGT :: modify( 1, 1, n, R[i].x, R[i].y, -1 );for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[ql + i + cntl - 1] = R[i];solve( mid + 1, r, ql + cntl, qr );solve( l, mid, ql, ql + cntl - 1 );
}signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1, op, x, y, k;i <= m;i ++ ) {scanf( "%lld %lld %lld %lld", &op, &x, &y, &k );q[i] = (node){ op, x, y, k, i };}solve( -n, n, 1, m );sort( q + 1, q + m + 1, []( node x, node y ) { return x.id < y.id; } );for( int i = 1;i <= m;i ++ ) if( q[i].op == 2 ) printf( "%lld\n", ans[q[i].id] );return 0;
}
[国家集训队]矩阵乘法
[国家集训队]矩阵乘法
数据结构用二维树状数组即可。
#include <bits/stdc++.h>
using namespace std;
#define maxn 505
#define maxm 400005
struct node { int op, l1, r1, l2, r2, k, id; }q[maxm], L[maxm], R[maxm];
int n, m;
int ans[maxm];namespace BIT {int t[maxn][maxn];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 )t[i][j] += v;}int ask( int x, int y ) {int cnt = 0;for( int i = x;i;i -= i & -i )for( int j = y;j;j -= j & -j )cnt += t[i][j];return cnt;}
}void solve( int l, int r, int ql, int qr ) {if( qr < ql ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) ans[q[i].id] = l; return; }int mid = l + r >> 1;int cntl = 0, cntr = 0;for( int i = ql;i <= qr;i ++ )if( q[i].op == 1 ) {if( q[i].k <= mid ) BIT :: add( q[i].l1, q[i].r1, 1 ), L[++ cntl] = q[i];else R[++ cntr] = q[i];}else {int cnt = BIT :: ask( q[i].l2, q[i].r2 ) - BIT :: ask( q[i].l1 - 1, q[i].r2 ) - BIT :: ask( q[i].l2, q[i].r1 - 1 ) + BIT :: ask( q[i].l1 - 1, q[i].r1 - 1 );if( q[i].k <= cnt ) L[++ cntl] = q[i];else q[i].k -= cnt, R[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) if( L[i].op == 1 ) BIT :: add( L[i].l1, L[i].r1, -1 );for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = R[i];solve( l, mid, ql, ql + cntl - 1 );solve( mid + 1, r, ql + cntl, qr );
}int main() {scanf( "%d %d", &n, &m );int cnt = 0;for( int i = 1;i <= n;i ++ )for( int j = 1, x;j <= n;j ++ ) {scanf( "%d", &x );q[++ cnt] = (node){ 1, i, j, i, j, x, cnt };}for( int i = 1, l1, r1, l2, r2, k;i <= m;i ++ ) {scanf( "%d %d %d %d %d", &l1, &r1, &l2, &r2, &k );q[++ cnt] = (node){ 2, l1, r1, l2, r2, k, cnt };}solve( 0, 1e9, 1, cnt );sort( q + 1, q + cnt + 1, []( node x, node y ) { return x.id < y.id; } );for( int i = 1;i <= cnt;i ++ ) if( q[i].op == 2 ) printf( "%d\n", ans[q[i].id] );return 0;
}
[THUPC2017] 天天爱射击
[THUPC2017] 天天爱射击
直接从子弹入手并不好做,这里稍微绕个弯,当我们知道所有木板最后一次被哪颗子弹射中,就能统计出每个子弹射穿的木板数。
所以应当以木板为因变量。考虑每次二分最后被射击的子弹 midmidmid,然后 check\text{check}check 前 midmidmid 个子弹能否射穿该木板。
发现这个可以整体二分做。
这里的 [l,r][l,r][l,r] 不再是权值,而是二分的子弹编号。
我们这里依然把子弹和木板放在同一个操作数组里面,子弹为类型 111,木板为类型 222。
注意两种操作的时间顺序安排,我们应该是先把所有子弹打了过后,再检查该木板被射中的次数是否达到承载值。
单点修改,区间查询,树状数组可做。
如果次数到达承载值,证明可能在更早时刻就已经被射穿,放入左边,否则放入右边,递归分治。
#include <bits/stdc++.h>
using namespace std;
#define maxn 400005
struct node { int op, x, y, k, id; }q[maxn], L[maxn], R[maxn];
int n, m;
int ans[maxn];namespace BIT {int t[maxn];void add( int i, int x ) { for( ;i < maxn;i += i & -i ) t[i] += x; }int ask( int i ) { int cnt = 0; for( ;i;i -= i & -i ) cnt += t[i]; return cnt; }
}void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) if( q[i].op == 2 ) ans[l] ++; return; }int mid = l + r >> 1;int cntl = 0, cntr = 0;for( int i = ql;i <= qr;i ++ )if( q[i].op == 1 ) {if( q[i].k <= mid ) BIT :: add( q[i].x, 1 ), L[++ cntl] = q[i];else R[++ cntr] = q[i];}else {int cnt = BIT :: ask( q[i].y ) - BIT :: ask( q[i].x - 1 );if( q[i].k <= cnt ) L[++ cntl] = q[i];else q[i].k -= cnt, R[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) if( L[i].op == 1 ) BIT :: add( L[i].x, -1 );for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = R[i];solve( l, mid, ql, ql + cntl - 1 );solve( mid + 1, r, ql + cntl, qr );
}int main() {scanf( "%d %d", &n, &m );for( int i = 1, x, y, k;i <= n;i ++ ) {scanf( "%d %d %d", &x, &y, &k );q[i + m] = (node){ 2, x, y, k, i + m };}for( int i = 1, x;i <= m;i ++ ) {scanf( "%d", &x );q[i] = (node){ 1, x, 0, i, i };}solve( 1, m + 1, 1, n + m );for( int i = 1;i <= m;i ++ ) printf( "%d\n", ans[i] );return 0;
}
[CTSC2018]混合果汁
[CTSC2018]混合果汁
显然我们可以整体二分最低的美味度 midmidmid。
然后把所有美味度 ≥mid\ge mid≥mid 的果汁都放入数据结构中。
但果汁有钱数和购买上限。
在二分后有个显然的贪心:越便宜的越先买。
我们可以用权值线段树,以单价为下标,然后记录总个数和总支付钱数。
查询时先看左区间,再看右区间。查询买最便宜的 lll 个的钱数是否在小朋友承受范围内。
但这里我们发现,好像这里的贡献提前去掉和前面几个模板题目不太一样。因为当 midmidmid 移动时可能会加入更便宜的果汁,所以我们不能在现在就把某些小朋友的钱数减去一部分。
那怎么办?——我们直接不减,就只是把小朋友不断分组即可。
每次撤销就类似莫队,用一个指针移动。仔细想想(可见下面代码)每一种果汁最多上线段树两次。
总时间复杂度是可以保证的。
本题我将果汁和小朋友两种操作分开存的,可能更直白一点。
这里的顺序不再是时间顺序,而是果汁美味度大小的顺序。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 100005
struct juice { int d, p, l; }g[maxn];
struct child { int g, l, id; }q[maxn], L[maxn], R[maxn];
int n, m, now;
int ans[maxn];namespace SGT {struct node { int cnt, sum; }t[maxn << 2];#define lson now << 1#define rson now << 1 | 1#define mid (l + r >> 1)void modify( int p, int x, int now = 1, int l = 1, int r = 1e5 ) {if( l == r ) { t[now].cnt += x; t[now].sum += x * p; return; }if( p <= mid ) modify( p, x, lson, l, mid );else modify( p, x, rson, mid + 1, r );t[now].cnt = t[lson].cnt + t[rson].cnt;t[now].sum = t[lson].sum + t[rson].sum;}int query( int x, int now = 1, int l = 1, int r = 1e5 ) {if( ! x ) return 0;if( l == r ) return l * x;if( x <= t[lson].cnt ) return query( x, lson, l, mid );else return t[lson].sum + query( x - t[lson].cnt, rson, mid + 1, r );}#undef lson#undef rson#undef mid
}void solve( int l, int r, int ql, int qr ) {if( ql > qr ) return;if( l == r ) { for( int i = ql;i <= qr;i ++ ) ans[q[i].id] = g[l].d; return; }int mid = l + r >> 1;while( now < mid ) ++ now, SGT :: modify( g[now].p, g[now].l );while( mid < now ) SGT :: modify( g[now].p, -g[now].l ), now --;int cntl = 0, cntr = 0;for( int i = ql;i <= qr;i ++ ) {if( SGT :: t[1].cnt < q[i].l ) R[++ cntr] = q[i];else if( SGT :: query( q[i].l ) <= q[i].g ) L[++ cntl] = q[i];else R[++ cntr] = q[i];}for( int i = 1;i <= cntl;i ++ ) q[ql + i - 1] = L[i];for( int i = 1;i <= cntr;i ++ ) q[ql + cntl + i - 1] = R[i];solve( l, mid, ql, ql + cntl - 1 );solve( mid + 1, r, ql + cntl, qr );
}signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%lld %lld %lld", &g[i].d, &g[i].p, &g[i].l );for( int i = 1;i <= m;i ++ ) scanf( "%lld %lld", &q[i].g, &q[i].l ), q[i].id = i;sort( g + 1, g + n + 1, []( juice x, juice y ) { return x.d > y.d; } );g[++ n] = { -1, 0, (int)1e18 };solve( 1, n, 1, m );for( int i = 1;i <= m;i ++ ) printf( "%lld\n", ans[i] );return 0;
}
【学习笔记】整体二分相关推荐
- OpenCL学习笔记——整体流程(向量相加)
OpenCL学习笔记--整体流程 OpenCL可以实现混合设备的并行计算,这些设备包括CPU,GPU,以及其他处理器,比如Cell处理器,DSP等.使用OpenCL编程,可以实现可以值得并行加速代码. ...
- 【算法学习】整体二分
我们开门见山,讲讲一道sb题: 给你一个数组,查这个数组的第x大元素. 排序?可以 二分?怎么做啊? 二分出一个mid,判断这个数组中有多少个数小于等于mid,如果个数大于等于x,就递归到[l,mid ...
- 【C++/C】【学习笔记】二分算法——处理“最小却最大”问题
第一篇博 希望能巩固自己学习成果的同时,为和我一样的初学者带来一些小小的帮助 二分 是 节气中的春分秋分 (咳 不开玩笑了 ====以下仅为博主个人理解 若出现问题或解释不当之处请大犇务必指正 感谢= ...
- 【算法学习笔记】二分查找法
二分查找法 二分查找的本质就是分治法,小时候有过这么一种猜数字的游戏,你在心中想一个大于0小于100的数字,然后我来提问,你只用回答是或者不是,比如"你想的数字比50大吗" 你说不 ...
- 经典题:poj2104-区间第k小 整体二分学习
写在前面 区间第k小 可以说是一个很经典的数据结构题了,这道题有很多种解法比如莫队离线.主席树.整体二分等等. 之前用莫队和主席树写过这道题,今天来学习一个以前不会的算法--整体二分. 因为最近遇到一 ...
- 【cdq分治】cdq分治与整体二分学习笔记Part2.cdq分治
上午的学习学会了整体二分,下午学了cdq分治 发现了二者的区别: 整体二分的主体是在不断地二分答案(把所有询问二分),而cdq分治则是在不断地二分操作. 当然同样的,cdq分治的复杂度也是与区间长度正 ...
- 【cdq分治】cdq分治与整体二分学习笔记Part1.整体二分
之所以把cdq分治和整体二分放在一起学习,是因为他们两个实在太像了-不管是做法还是代码- 感觉整体二分可能会比cdq分治稍微简单那么一点点?所以先学整体二分. 整体二分是对答案进行二分,其具体操作如下 ...
- RocketMQ学习笔记(7)----RocketMQ的整体架构
1. RocketMQ主要的9个模块,如图: 2. 模块介绍 1. rocketmq-common:通用的常量枚举,基类方法或者数据结构,按描述的目标来分包,通俗易懂.报名有admin,consume ...
- OpenCV学习笔记四-image的一些整体操作
title: OpenCV学习笔记四-image的一些整体操作 categories: 编程 date: 2019-08-08 12:50:47 tags: OpenCV image的一些操作 sP4 ...
最新文章
- 深度学习多变量时间序列预测:LSTM算法构建时间序列多变量模型预测交通流量+代码实战
- 802.3和以太网帧结构-路由交换原理1-【HCNA笔记】
- python关闭浏览器、未过期的session_session.cookie_lifetime = 0时,为什么会话在浏览器关闭时不会过期?...
- 【图像处理】——图像滤波(Python+opencv实现三种方法:均值滤波、中值滤波、高斯滤波等)
- 面试中Handler这些必备知识点你都知道吗?面试真题解析
- 十四.激光和惯导LIO-SLAM框架学习之惯导内参标定
- win8 开发之旅(3) --五子棋游戏开发 前奏
- 《从零开始学Swift》学习笔记(Day 14)——字符串的插入、删除和替换
- cad渐开线齿轮轮廓绘制_CAD画齿轮的渐开线程序 (lsp)和渐开线齿轮关系
- 【笔记】双线性池化(Bilinear Pooling)详解、改进及应用
- 3.5.3 连接偏移量管理器
- Word下修改默认新建样式模板方法
- scikit-learn:回归分析—一元非线性回归
- 推荐 :一小时建立数据分析平台
- 解决cannot find grldr in all devices问题
- p=p-next 表达了什么意思
- 3周个人错误和知识弱点盲点{随时更新}
- Linux内核的misc框架
- html 中输入千分号、万分号
- 初级计算机硬件试题,新版初级计算机操作员应知考试试题
热门文章
- 美国劳工部揭露中国女人大数据,看完彻底傻眼了……
- 用java写注册表单_利用HTML表单标签编写一个注册页面
- 学计算机与学英语作文,初二英语作文(关于计算机与学习)
- pdf编辑软件adobe acrobat_分享一款PDF编辑和阅读软件Acrobat
- 获取两个数据的交集_MySQL交集和差集的实现方法
- python——学习笔记3
- leetcode300. 最长递增子序列
- html中的时间代码怎么写,html网页代码中的时间样式怎样设置
- [JavaWeb-XML]XML概述
- 《C++ Primer》14.3.1节练习