整体二分的基本思想是,二分出一个 ,然后遍历所有的询问,每个询问都 check 一下 mid 是偏大了还是偏小了。把偏大的询问分成一组,偏小的询问分成另一组。然后这两组询问再分别二分
https://www.luogu.com.cn/problem/P3332
题目描述
你需要维护 个可重整数集,集合的编号从 到 。
这些集合初始都是空集,有 个操作:
1 l r c
:表示将 加入到编号在 内的集合中2 l r c
:表示查询编号在 内的集合的并集中,第 大的数是多少。注意可重集的并是不去除重复元素的,如 。
数据范围
操作中
操作中 ,第 大的数存在
笔记
cpp123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
typedef pair<int,int> pii;
#define _ 50004
int n,arr[_],m,sum[_<<2],tag[_<<2];
inline void pushup(int k){sum[k]=sum[ls(k)]+sum[rs(k)];}
inline void pushadd(int k,int l,int r,int v){sum[k]+=v*(r-l+1),tag[k]+=v;}
inline void pushdown(int k,int l,int r,int mid){if(tag[k])pushadd(ls(k),l,mid,tag[k]),pushadd(rs(k),mid+1,r,tag[k]),tag[k]=0;}
void modify(int k,int l,int r,int x,int y,int v){
if(l>=x&&r<=y) return pushadd(k,l,r,v),void();
int mid=(l+r)>>1;
pushdown(k,l,r,mid);
if(x<=mid) modify(ls(k),l,mid,x,y,v);
if(y>mid) modify(rs(k),mid+1,r,x,y,v);
pushup(k);
}
int query(int k,int l,int r,int x,int y){
if(l>=x&&r<=y) return sum[k];
int mid=(l+r)>>1,res=0;
pushdown(k,l,r,mid);
if(x<=mid) res+=query(ls(k),l,mid,x,y);
if(y>mid) res+=query(rs(k),mid+1,r,x,y);
return res;
}
struct Q{int l,r,x,id,ans,op;} L[_],R[_],q[_];
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) q[i].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].x>mid) modify(1,1,n,q[i].l,q[i].r,1),R[++cntr]=q[i];
else L[++cntl]=q[i];
}else{
int cnt=query(1,1,n,q[i].l,q[i].r);
if(q[i].x<=cnt) R[++cntr]=q[i];
else q[i].x-=cnt,L[++cntl]=q[i];
}
}
for(int i=ql;i<=qr;i++) if(q[i].op==1&&q[i].x>mid) modify(1,1,n,q[i].l,q[i].r,-1);
for(int i=1;i<=cntl;i++) q[ql-1+i]=L[i];
for(int i=1;i<=cntr;i++) q[ql+cntl-1+i]=R[i];
solve(l,mid,ql,ql+cntl-1);
solve(mid+1,r,ql+cntl,qr);
}
inline void subtask(){
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>q[i].op>>q[i].l>>q[i].r>>q[i].x,q[i].id=i;
solve(-n,n,1,m);
sort(q+1,q+m+1,[](Q x,Q y){return x.id<y.id;});
for(int i=1;i<=m;i++) if(q[i].op==2) cout<<q[i].ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);
int t=1;//cin>>t;
while(t--) subtask();
return 0;
}
因为我们判断 mid 是偏大了还是偏小了,需要知道比 mid 大的数有多少,因此我们把大于 的 值加入线段树。并且,如果询问需要归到 中,那么也只有 值大于 的操作一会影响到这些询问,因此 值大于 的操作也归到 中
https://www.luogu.com.cn/problem/P3527
题目描述
有 个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为 份(第 份和第 份相邻),第 份上有第 个国家的太空站。
这个星球经常会下陨石雨。BIU 已经预测了接下来 场陨石雨的情况。
BIU 的第 个成员国希望能够收集 单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。
输入格式
第一行是两个数 。
第二行有 个数,第 个数 表示第 段轨道上有第 个国家的太空站。
第三行有 个数,第 个数 表示第 个国家希望收集的陨石数量。
第四行有一个数 ,表示 BIU 预测了接下来的 场陨石雨。 接下来 行,每行有三个数 ,表示第 场陨石雨的发生地点在从 顺时针到 的区间中(如果 ,则是 ,否则就是 ),向区间中的每个太空站提供 单位的陨石样本。
数据范围
;
;
笔记
把环形的区间加拆成两个操作,然后整体二分即可。整体二分时直接暴力计算每个询问是否满足要求貌似是可以的
cpp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
typedef pair<int,int> pii;
#define _ 900005
int arr[_],n,m;
inline int lowbit(int x){return x&-x;}
inline void modify(int x,int v){while(x<=m) arr[x]+=v,x+=lowbit(x);}
inline int query(int x,int res=0){while(x) res+=arr[x],x-=lowbit(x);return res;}
struct Q{int l,r,id,ans,op,x,need;} q[_],L[_],R[_];
vector<int> g[_];
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==0) q[i].ans=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].id<=mid) modify(q[i].l,q[i].x),modify(q[i].r+1,-q[i].x),L[++cntl]=q[i];
else R[++cntr]=q[i];
}else{
int s=0;
for(auto to:g[q[i].x]){
s+=query(to);
if(s>=q[i].need) break;
}
if(s>=q[i].need) L[++cntl]=q[i];
else q[i].need-=s,R[++cntr]=q[i];
}
}
for(int i=ql;i<=qr;i++) if(q[i].op==1&&q[i].id<=mid) modify(q[i].l,-q[i].x),modify(q[i].r+1,q[i].x);
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 need[_];
inline void subtask(){
cin>>n>>m;
for(int i=1,x;i<=m;i++) cin>>x,g[x].push_back(i);
for(int i=1;i<=n;i++) cin>>need[i];
int totq,cntq=0;cin>>totq;
for(int i=1;i<=totq;i++){
int l,r,x;cin>>l>>r>>x;
if(l<=r) q[++cntq].l=l,q[cntq].r=r,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x;
else q[++cntq].l=1,q[cntq].r=r,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x,q[++cntq].l=l,q[cntq].r=m,q[cntq].op=1,q[cntq].id=i,q[cntq].x=x;
}
for(int i=1;i<=n;i++) q[++cntq].id=i,q[cntq].x=i,q[cntq].need=need[i];
//for(int i=1;i<=cntq;i++) debug(q[i].op),debug(q[i].l),debug(q[i].r);
solve(1,totq+1,1,cntq);
sort(q+1,q+cntq+1,[](Q x,Q y){return x.id<y.id;});
for(int i=1;i<=cntq;i++){
if(q[i].op) continue;
if(q[i].ans==totq+1) cout<<"NIE"<<endl;
else cout<<q[i].ans<<endl;
}
}
signed main(){
ios::sync_with_stdio(false);
int t=1;//cin>>t;
while(t--) subtask();
return 0;
}