-
前置知识 : 二分,一些数据结构如树状数组
-
用途 : 用于解决多次可二分可离线的询问。
可以使用整体二分解决的题目应具有以下性质 :
- 询问的答案具有可二分性。
- 修改对判定答案的贡献互相独立,修改之间互不影响效果。
- 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值。
- 贡献满足交换律,结合律,具有可加性。
- 题目允许使用离线算法。
——许昊然《浅谈数据结构题几个非经典解法》
大体的流程是这样的 :
- 先将所有操作用数组记录下来,然后按时间顺序分治。
- 假设递归函数为work,那么一般会有4个参数 : \(l,r\)分别为答案左端点、右端点(就是二分答案时的left和right), \(ql,qr\)分别为操作的左端点,右端点(因为只有\(ql\sim qr\)的操作会对答案产生贡献)。
- 如果\(ql>qr\),直接return。如果\(l=r\),那么\(ql\sim qr\)之间查询操作的答案就是l,记录答案,return掉。
- 二分答案,记\(mid = \frac{l+r}{2}\)。
- 遍历\(ql\sim qr\)的操作。
- 若为修改操作 : 如果当前操作影响的点会影响到mid,那么将其按照时间顺序记入一个临时数组\(lq\)中,用数据结构更改贡献,反之,那么将其按照时间顺序记入另一个临时数组\(rq\)中。
- 若为查询操作 : 如果当前操作的答案小于mid,将其按照时间顺序记入\(lq\)中,反之,减去贡献,按照时间顺序扔到\(rq\)中。
- 然后将\(lq\)所有更改操作的贡献还原,是一个一个复原,不是直接memset,不然复杂度是错误的。
- 然后将\(lq,rq\)依次并入最一开始的操作数组\(q\)中。
- 递归下一层,分别为\(work(l,mid,ql,ql+q1-1),work(mid+1,r,ql+q1,qr)\)
优势 : 一般常数较小如果您是大常数体质当我没说,比较好写,空间复杂度优秀。
劣势 : 必须离线。
常见的一般就是求k大值或者是可以通过题意转化成k大值的,还有求中位数的,套个cdq硬c树套树的?(雾)
但一般的题都是求k大值哎。
例题 :
主席树板子当然不能用主席树做啦
一看题 : 区间rank,典型的可二分,再一看,主席树板子竟然不强制在线? 整体二分,启动。
大概就是像上面的流程走就行了。
反正可以离线,那么就离散化,然后传入的值为由\(-INF,INF\)变成\(1,\text{不同值的数量}\),提升效率。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,a[N];
class BIT{
private:
int tree[N];
#define lowbit(x) (x&(-x))
public:
int mx;
inline void update(int pos,int val){
for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;
}
inline int query(int pos){
int res = 0;
for(int i = pos; i;i -= lowbit(i))
res += tree[i];
return res;
}
}T;
struct node{int l,r,k,id,op;}q[N<<1],lq[N<<1],rq[N<<1];
int cnt,ans[N];
void work(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,p1 = 0,p2 = 0;
for(int i = ql;i <= qr; ++i){
if(q[i].op == 1){
if(q[i].k <= mid) T.update(q[i].l,1),lq[++p1] = q[i];//操作可以影响到当前二分的答案。记录贡献,存入左边
else rq[++p2] = q[i];//影响不到答案,存入右边
}
else{
int res = T.query(q[i].r) - T.query(q[i].l - 1);//查询
if(res >= q[i].k) lq[++p1] = q[i];//答案要更小,记入左边。
else{
q[i].k -= res;
rq[++p2] = q[i];//答案要更大,将以前的贡献减去,记入右边
}
}
}
for(int i = 1;i <= p1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-1);//将贡献删去,注意不要用memset
for(int i = 1;i <= p1; ++i) q[i + ql - 1] = lq[i];
for(int i = 1;i <= p2; ++i) q[i + ql + p1 -1] = rq[i];//合并
work(l,mid,ql,ql+p1-1);work(mid+1,r,ql+p1,qr);//递归进下一层
}
inline void solve(){
cin>>n>>m;
for(int i = 1;i <= n; ++i) cin>>a[i];
vector<int> num;
for(int i = 1;i <= n; ++i) num.emplace_back(a[i]);
sort(num.begin(),num.end());
for(int i = 1;i <= n; ++i)
a[i] = lower_bound(num.begin(),num.end(),a[i]) - num.begin() + 1;
for(int i = 1;i <= n; ++i)
q[++cnt] = {i,i,a[i],0,1};
for(int i = 1,l,r,k;i <= m; ++i){
cin>>l>>r>>k;
q[++cnt] = {l,r,k,i,2};
}
T.mx = n;
work(1,num.size(),1,cnt);
for(int i = 1;i <= m; ++i) cout<<num[ans[i]-1]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
还有好多经验可以加
还是板板,变成二维树状数组即可。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 510;
inline int lowbit(int x){return (x&(-x));}
class BIT{
private:
int tree[N];
public:
int mx;
inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T[N];
int cnt,n,m,a[N][N],ans[N];
inline void update(int x,int y,int val){
for(int i = x;i <= n;i += lowbit(i)) T[i].update(y,val);
}
inline int query(int x,int y){
int res = 0;
for(int i = x; i;i -= lowbit(i)) res += T[i].query(y);
return res;
}
struct node{int op,l,r,k,x1,x2,y1,y2,id;}q[400010],lq[400010],rq[400010];
inline void work(int l,int r,int ql,int qr){
if(ql > qr) return;
if(l == r){
// cerr<<l<<": "<<ql<<' '<<qr<<'\n';
for(int i = ql;i <= qr; ++i){
// cerr<<q[i].op<<' ';
// if(q[i].op == 1) cerr<<q[i].l<<' '<<q[i].r<<' '<<q[i].k<<'\n';
// else cerr<<q[i].x1<<' '<<q[i].y1<<' '<<q[i].x2<<' '<<q[i].y2<<'\n';
if(q[i].op == 2) ans[q[i].id] = l;
}
// cerr<<"\n\n";
return;
}
int mid = (l + r) >> 1,q1 = 0,q2 = 0;
for(int i = ql;i <= qr; ++i){
if(q[i].op == 1){
if(q[i].k <= mid) update(q[i].l,q[i].r,1),lq[++q1] = q[i];
else rq[++q2] = q[i];
}
else{
int res = query(q[i].x2,q[i].y2) - query(q[i].x1-1,q[i].y2) - query(q[i].x2,q[i].y1-1) + query(q[i].x1-1,q[i].y1-1);
if(res >= q[i].k) lq[++q1] = q[i];
else{
q[i].k -= res;
rq[++q2] = q[i];
}
}
}
for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) update(lq[i].l,lq[i].r,-1);
for(int i = 1;i <= q1; ++i) q[i + ql - 1] = lq[i];
for(int i = 1;i <= q2; ++i) q[i + ql + q1 - 1] = rq[i];
work(l,mid,ql,ql+q1-1);
work(mid+1,r,ql+q1,qr);
}
inline void solve(){
cin>>n>>m;
for(int i = 1;i <= n; ++i) T[i].mx = n;
vector<int> num;
for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) cin>>a[i][j],num.emplace_back(a[i][j]);
// for(int i = 1;i <= n; ++i){
// for(int j = 1;j <= n; ++j){
// cout<<query(i,j)<<' ';
// }
// cout<<'\n';
// }
// exit(0);
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) a[i][j] = lower_bound(num.begin(),num.end(),a[i][j]) - num.begin() + 1;
for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) cnt++,q[cnt].op = 1,q[cnt].l = i,q[cnt].r = j,q[cnt].k = a[i][j];
for(int i = 1;i <= m; ++i){
cnt++;q[cnt].op = 2;q[cnt].id = i;
cin>>q[cnt].x1>>q[cnt].y1>>q[cnt].x2>>q[cnt].y2>>q[cnt].k;
}
work(1,num.size(),1,cnt);
for(int i = 1;i <= m; ++i) cout<<num[ans[i]-1]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
修改+区间rank,将修改分成两个操作 : 将原来的减去,将现在的加上。
然后套板板就好啦。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e5 + 10;
struct node{int op,l,r,k,x,ad,pos,id;}q[N*3],lq[N*3],rq[N*3];
int cnt,n,a[N],m,ans[N];
class BIT{
private:int tree[N];inline int lowbit(int x){return (x&(-x));}
public: int mx;
inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T;
void work(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,q1 = 0,q2 = 0;
for(int i = ql;i <= qr; ++i){
if(q[i].op == 1){
if(q[i].x <= mid) T.update(q[i].pos,q[i].ad),lq[++q1] = q[i];
else rq[++q2] = q[i];
}
else{
int res = T.query(q[i].r) - T.query(q[i].l - 1);
if(res >= q[i].k) lq[++q1] = q[i];
else{
q[i].k -= res;
rq[++q2] = q[i];
}
}
}
for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) T.update(lq[i].pos,-lq[i].ad);
for(int i = 1;i <= q1; ++i) q[ql + i - 1] = lq[i];
for(int i = 1;i <= q2; ++i) q[ql + i + q1 - 1] = rq[i];
work(l,mid,ql,ql+q1-1);work(mid+1,r,ql+q1,qr);
}
inline void solve(){
cin>>n>>m;T.mx = n;
vector<int> num;
for(int i = 1;i <= n; ++i){
cin>>a[i];cnt++;
q[cnt].op = 1,q[cnt].x = a[i],q[cnt].ad = 1;q[cnt].pos = i;
num.push_back(a[i]);
}
auto Get = [](char x) -> int{return (x == 'C'?1:2);};
int p = 0;
for(int i = 1;i <= m; ++i){
char opt;cin>>opt;
int op = Get(opt);
if(op == 1){
int x,y;cin>>x>>y;
cnt++;q[cnt].op = 1,q[cnt].x = a[x],q[cnt].ad = -1;q[cnt].pos = x;
a[x] = y;num.emplace_back(y);
cnt++;q[cnt].op = 1,q[cnt].x = a[x],q[cnt].ad = 1;q[cnt].pos = x;
}
else{
int l,r,k;cin>>l>>r>>k;p++;
cnt++;q[cnt].op = 2,q[cnt].l = l,q[cnt].r = r,q[cnt].k = k;q[cnt].id = p;
}
}
sort(num.begin(),num.end());
num.erase(unique(num.begin(),num.end()),num.end());
for(int i = 1;i <= cnt; ++i)
if(q[i].op == 1)
q[i].x = lower_bound(num.begin(),num.end(),q[i].x) - num.begin() + 1;
work(1,num.size(),1,cnt);
for(int i = 1;i <= p; ++i) cout<<num[ans[i] - 1]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
整体二分肆意薄纱树套树反正我线段树套pbds的平衡树只拿了85pts
还是板板,把第一道题的代码贺下来改改就可过。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,a[N];
class BIT{
private:
int tree[N];
#define lowbit(x) (x&(-x))
public:
int mx;
inline void update(int pos,int val){
for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;
}
inline int query(int pos){
int res = 0;
for(int i = pos; i;i -= lowbit(i))
res += tree[i];
return res;
}
}T;
struct node{int l,r,k,id,op;}q[N<<1],lq[N<<1],rq[N<<1];
int cnt,ans[N];
void work(int l,int r,int ql,int qr){
if(ql > qr) return;
if(l == r){
// cerr<<l<<'\n';
for(int i = ql;i <= qr; ++i)
if(q[i].op == 2) ans[l]++;
return;
}
int mid = (l + r) >> 1,p1 = 0,p2 = 0;
for(int i = ql;i <= qr; ++i){
if(q[i].op == 1){
if(q[i].k <= mid) T.update(q[i].l,1),lq[++p1] = q[i];
else rq[++p2] = q[i];
}
else{
int res = T.query(q[i].r) - T.query(q[i].l - 1);
if(res >= q[i].k) lq[++p1] = q[i];
else{
q[i].k -= res;
rq[++p2] = q[i];
}
}
}
for(int i = 1;i <= p1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-1);
for(int i = 1;i <= p1; ++i) q[i + ql - 1] = lq[i];
for(int i = 1;i <= p2; ++i) q[i + ql + p1 -1] = rq[i];
work(l,mid,ql,ql+p1-1);
work(mid+1,r,ql+p1,qr);
}
int l[N],r[N],t[N];
inline void solve(){
cin>>n>>m;int mx = 0;
for(int i = 1;i <= n; ++i) cin>>l[i]>>r[i]>>t[i],mx = max(mx,r[i]);
for(int i = 1,x;i <= m; ++i){
cin>>x;mx = max(x,mx);
q[++cnt] = {x,0,i,0,1};
}
T.mx = mx;
for(int i = 1;i <= n; ++i) q[++cnt] = {l[i],r[i],t[i],i,2};
work(1,m+1,1,cnt);
for(int i = 1;i <= m; ++i) cout<<ans[i]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
经典题。
就是树状数组区间修改,单点查询,对于每个星球的查询暴力就行了。
注意树状数组会爆ll,开ull即可。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 3e5 + 10;
#define int ull
int n,m,cnt,a[N],p,ans[N];
vector<int> have[N];
struct node{int l,r,k,id,op;}q[N*3],lq[N*3],rq[N*3];
class BIT{
private:ll tree[N];inline int lowbit(int x){return (x&(-x));}
public:int mx;
inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
inline ll query(int pos){ll res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T;
void work(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,q1 = 0,q2 = 0;
for(int i = ql;i <= qr; ++i){
if(q[i].op == 1)
if(q[i].id <= mid) T.update(q[i].l,q[i].k),T.update(q[i].r+1,-q[i].k),lq[++q1] = q[i];
else rq[++q2] = q[i];
else{
ll res = 0;
for(auto x : have[q[i].id]) res += T.query(x);
if(res >= q[i].k) lq[++q1] = q[i];
else q[i].k -= res,rq[++q2] = q[i];
}
}
for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-lq[i].k),T.update(lq[i].r+1,lq[i].k);
for(int i = 1;i <= q1; ++i) q[i + ql - 1] = lq[i];
for(int i = 1;i <= q2; ++i) q[i + ql + q1 - 1] = rq[i];
work(l,mid,ql,ql+q1-1);work(mid+1,r,ql+q1,qr);
}
inline void solve(){
cin>>n>>m;T.mx = m;
for(int i = 1,x;i <= m; ++i) cin>>x,have[x].emplace_back(i);
for(int i = 1;i <= n; ++i) cin>>a[i];
cin>>p;
for(int i = 1,l,r,k;i <= p; ++i){
cin>>l>>r>>k;
if(l <= r) q[++cnt] = {l,r,k,i,1};
else q[++cnt] = {l,m,k,i,1},q[++cnt] = {1,r,k,i,1};
}
for(int i = 1;i <= n; ++i) q[++cnt] = {i,i,a[i],i,2};
work(1,p+1,1,cnt);
for(int i = 1;i <= n; ++i) if(ans[i] <= p) cout<<ans[i]<<'\n';else cout<<"NIE\n";
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}