整体二分小结
摘抄部分 2013 许昊然论文-《浅谈数据结构题的几个非经典解法》
特么的论文用了啥 j8 加密编码, 还不能复制。。又不是不标出处。
时间复杂度
T(LQ,Lans):=当前待二分询问区间长度为LQ,待二分答案区间长度为Lans的时间复杂度
且单次处理的复杂度为O(f(n)),则有:
T(LQ,Lans)=T(S0,Lans2)+T(LQ−S0,Lans2)+O(f(LQ))
解之得:T(|Q|,|C|)≤O(f(Q))logC,|Q|为询问序列长度,|C|为数据规模
PS:证明要求O(f(n))≥O(n)才成立,由于询问序列长度已经达到n,单次处理复杂度几乎不可能小于O(n),当然真不满足具体问题具体分体即可
时间复杂度分析举例
k−smallest number的单次处理复杂度为O(LQlogN)
于是总复杂度为O((N+Q)logNlogC)
题意:
N≤105的序列,|Ai|≤109,Q≤5×103次查询
l,r,k:查询[l,r]的k−smallest数
代码:
//
// Created by TaoSama on 2016-02-14
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int Q = 1e4 + 10;
struct Query {
int x, y, k;
int id, type;
void print() {
printf("%d %d %d %d %d\n", x, y, k, id, type);
}
} q[N + Q], q1[N + Q], q2[N + Q];
struct BIT {
int n, b[N];
void init(int _n) {n = _n; memset(b, 0, sizeof b);}
void add(int i, int x) {
for(; i <= n; i += i & -i) b[i] += x;
}
int sum(int i) {
int ret = 0;
for(; i; i -= i & -i) ret += b[i];
return ret;
}
} bit;
int n, m, a[N];
int ans[Q];
void solve(int qL, int qR, int l, int r) {
if(qL > qR) return;
if(l == r) {
for(int i = qL; i <= qR; ++i)
if(q[i].type == 2) ans[q[i].id] = l;
return;
}
int m = l + r >> 1;
//check m
int f = 0, g = 0;
for(int i = qL; i <= qR; ++i) {
if(q[i].type == 1) {
if(q[i].x <= m) {
bit.add(q[i].id, q[i].y);
q1[f++] = q[i];
} else q2[g++] = q[i];
} else {
int small = bit.sum(q[i].y) - bit.sum(q[i].x - 1);
if(small >= q[i].k) q1[f++] = q[i];
else {
q[i].k -= small;
q2[g++] = q[i];
}
}
}
//clear
for(int i = 0; i < f; ++i)
if(q1[i].type == 1) bit.add(q1[i].id, -q1[i].y);
//copy back
memcpy(q + qL, q1, f * sizeof(Query));
memcpy(q + qL + f, q2, g * sizeof(Query));
solve(qL, qL + f - 1, l, m);
solve(qL + f, qR, m + 1, r);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d", &n, &m) == 2) {
bit.init(n);
int idx = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", a + i);
q[++idx] = (Query) {a[i], 1, INF, i, 1};
}
for(int i = 1; i <= m; ++i) {
int x, y, k; scanf("%d%d%d", &x, &y, &k);
q[++idx] = (Query) {x, y, k, i, 2};
}
solve(1, idx, -INF, INF);
for(int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
}
return 0;
}
题意:
N≤5×104的序列,0<Ai≤109,Q≤5×104次查询
Q l r k:查询[l,r]的k−smallest数
C i x:把Ai改成x
分析:
把修改分解成一次删除和一次添加,然后照上面那个题做法即可
代码:
//
// Created by TaoSama on 2016-02-14
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 5e4 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int Q = 2e4 + 10;
struct Query {
int x, y, k;
int id, type;
void print() {
printf("%d %d %d %d %d\n", x, y, k, id, type);
}
} q[N + Q], q1[N + Q], q2[N + Q];
struct BIT {
int n, b[N];
void init(int _n) {n = _n; memset(b, 0, sizeof b);}
void add(int i, int x) {
for(; i <= n; i += i & -i) b[i] += x;
}
int sum(int i) {
int ret = 0;
for(; i; i -= i & -i) ret += b[i];
return ret;
}
} bit;
int n, m, a[N];
int ans[Q];
void solve(int qL, int qR, int l, int r) {
if(qL > qR) return;
if(l == r) {
for(int i = qL; i <= qR; ++i)
if(q[i].type == 2) ans[q[i].id] = l;
return;
}
int m = l + r >> 1;
//check m
int f = 0, g = 0;
for(int i = qL; i <= qR; ++i) {
if(q[i].type == 1) {
if(q[i].x <= m) {
bit.add(q[i].id, q[i].y);
q1[f++] = q[i];
} else q2[g++] = q[i];
} else {
int small = bit.sum(q[i].y) - bit.sum(q[i].x - 1);
if(small >= q[i].k) q1[f++] = q[i];
else {
q[i].k -= small;
q2[g++] = q[i];
}
}
}
//clear
for(int i = 0; i < f; ++i)
if(q1[i].type == 1) bit.add(q1[i].id, -q1[i].y);
//copy back
memcpy(q + qL, q1, f * sizeof(Query));
memcpy(q + qL + f, q2, g * sizeof(Query));
solve(qL, qL + f - 1, l, m);
solve(qL + f, qR, m + 1, r);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
int t; scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
bit.init(n);
int idx = 0, cnt = 0;
for(int i = 1; i <= n; ++i) {
scanf("%d", a + i);
q[++idx] = (Query) {a[i], 1, INF, i, 1};
}
for(int i = 1; i <= m; ++i) {
char op[2]; int x, y, k;
scanf("%s%d%d", op, &x, &y);
if(*op == 'Q') {
scanf("%d", &k);
q[++idx] = (Query) {x, y, k, ++cnt, 2};
} else {
q[++idx] = (Query) {a[x], -1, INF, x, 1};
a[x] = y;
q[++idx] = (Query) {a[x], 1, INF, x, 1};
}
}
// for(int i = 1; i <= idx; ++i) q[i].print();
solve(1, idx, 0, INF);
for(int i = 1; i <= cnt; ++i)
printf("%d\n", ans[i]);
}
return 0;
}
题意:
N≤5×104,初始为空序列,Q≤5×104次查询
1 a b c:将[l,r]的每个位置插入一个c,|c|≤N
2 a b c:查询[l,r]中c−largest的数,c≤LL_MAX
分析:
将插入的c变为n−c+1,此时查询c−largest就变成了c−smallest
问题变得与上面题目类似了,至于区间插入只要线段树区间更新即可
当然输出结果的时候要还原数,即ans=n−ans+1
代码:
//
// Created by TaoSama on 2016-02-15
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int Q = 5e4 + 10;
typedef long long LL;
struct Query {
int a, b; LL c;
int id;
} q[Q], q1[Q], q2[Q];
struct SegTree {
LL sum[N << 2], add[N << 2];
void pushUp(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushDown(int rt, int m) {
if(add[rt]) {
sum[rt << 1] += (m - m / 2) * add[rt];
sum[rt << 1 | 1] += (m / 2) * add[rt];
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
add[rt] = 0;
}
}
void update(int L, int R, int v, int l, int r, int rt) {
if(L <= l && r <= R) {
sum[rt] += v * (r - l + 1);
add[rt] += v;
return;
}
pushDown(rt, r - l + 1);
int m = l + r >> 1;
if(L <= m) update(L, R, v, l, m, rt << 1);
if(R > m) update(L, R, v, m + 1, r, rt << 1 | 1);
pushUp(rt);
}
LL query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) return sum[rt];
pushDown(rt, r - l + 1);
int m = l + r >> 1;
LL ret = 0;
if(L <= m) ret += query(L, R, l, m, rt << 1);
if(R > m) ret += query(L, R, m + 1, r, rt << 1 | 1);
return ret;
}
} T;
int n, m;
int ans[Q];
void solve(int L, int R, int l, int r) {
if(L > R) return;
if(l == r) {
for(int i = L; i <= R; ++i)
if(q[i].id > 0) ans[q[i].id] = l;
return;
}
int m = l + r >> 1;
int f = 0, g = 0;
for(int i = L; i <= R; ++i) {
if(q[i].id < 0) {
if(q[i].c <= m) {
T.update(q[i].a, q[i].b, 1, 1, n, 1);
// printf("add %d: %d %d %lld %d\n", i, q[i].a, q[i].b, q[i].c, m);
q1[f++] = q[i];
} else q2[g++] = q[i];
} else {
LL small = T.query(q[i].a, q[i].b, 1, n, 1);
// printf("query %d: %d %d %lld small=%lld\n", i,
// q[i].a, q[i].b, q[i].c, small);
if(small >= q[i].c) q1[f++] = q[i];
else {
q[i].c -= small;
q2[g++] = q[i];
}
}
}
for(int i = 0; i < f; ++i)
if(q1[i].id < 0) T.update(q1[i].a, q1[i].b, -1, 1, n, 1);
memcpy(q + L, q1, f * sizeof(Query));
memcpy(q + L + f, q2, g * sizeof(Query));
solve(L, L + f - 1, l, m);
solve(L + f, R, m + 1, r);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d", &n, &m) == 2) {
memset(&T, 0, sizeof T);
int cnt = 0;
for(int i = 1; i <= m; ++i) {
int op, a, b; LL c; scanf("%d%d%d%lld", &op, &a, &b, &c);
if(op == 1) c = n - c + 1; //change number so as to be k-th
q[i] = (Query) {a, b, c, op == 1 ? -1 : ++cnt};
}
solve(1, m, 1, 2 * n + 1);
for(int i = 1; i <= cnt; ++i)
printf("%d\n", n - ans[i] + 1); //change back
}
return 0;
}
题意:
给定N∗N的矩阵,N≤500,Q≤6×104次询问
x1,y1,x2,y2,k:查询以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的k−smallest数
分析:
xhr论文里的一个题,更新变成了二维BIT而已
代码:
//
// Created by TaoSama on 2016-02-15
// Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>
using namespace std;
#define pr(x) cout << #x << " = " << x << " "
#define prln(x) cout << #x << " = " << x << endl
const int N = 5e2 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const int Q = 6e4 + 10;
struct Query {
int x1, y1, x2, y2, k;
int id;
} q[N * N + Q], q1[N * N + Q], q2[N * N + Q];
struct BIT {
int n, b[N][N];
void init(int _n) {n = _n; memset(b, 0, sizeof b);}
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)
b[i][j] += v;
}
int sum(int x, int y) {
int ret = 0;
for(int i = x; i; i -= i & -i)
for(int j = y; j; j -= j & -j)
ret += b[i][j];
return ret;
}
int sum(int x1, int y1, int x2, int y2) {
return sum(x2, y2) - sum(x1 - 1, y2) - sum(x2, y1 - 1)
+ sum(x1 - 1, y1 - 1);
}
} bit;
int n, m, ans[Q];
void solve(int L, int R, int l, int r) {
if(L > R) return;
if(l == r) {
for(int i = L; i <= R; ++i)
if(q[i].id) ans[q[i].id] = l;
return;
}
int m = l + r >> 1;
int f = 0, g = 0;
for(int i = L; i <= R; ++i) {
if(!q[i].id) {
if(q[i].k <= m) {
bit.add(q[i].x1, q[i].y1, 1);
q1[f++] = q[i];
} else q2[g++] = q[i];
} else {
int small = bit.sum(q[i].x1, q[i].y1, q[i].x2, q[i].y2);
if(small >= q[i].k) q1[f++] = q[i];
else {
q[i].k -= small;
q2[g++] = q[i];
}
}
}
for(int i = 0; i < f; ++i)
if(!q1[i].id) bit.add(q1[i].x1, q1[i].y1, -1);
memcpy(q + L, q1, f * sizeof(Query));
memcpy(q + L + f, q2, g * sizeof(Query));
solve(L, L + f - 1, l, m);
solve(L + f, R, m + 1, r);
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
// freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
ios_base::sync_with_stdio(0);
while(scanf("%d%d", &n, &m) == 2) {
bit.init(n);
int idx = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
int x; scanf("%d", &x);
q[++idx] = (Query) {i, j, 0, 0, x, 0};
}
}
for(int i = 1; i <= m; ++i) {
int x1, y1, x2, y2, k;
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &k);
q[++idx] = (Query) {x1, y1, x2, y2, k, i};
}
solve(1, idx, 1, INF);
for(int i = 1; i <= m; ++i)
printf("%d\n", ans[i]);
}
return 0;
}