整体二分

OI wiki

个人感觉整体二分就像归并的逆操作。

P3527 [POI2011]MET-Meteors & LOJ2169. 「POI2011 R3 Day2」流星 Meteors

P1527 [国家集训队]矩阵乘法

P3332 [ZJOI2013]K大数查询

自己去看原题吧

树套树 ×,整体二分 (离线算法常数吊打树套树)。

我们每次可以定一个阈(yù)值 mid,通过线段树判断每一个询问的答案是否 mid

  • 若是,则归到第二类。

  • 若否,则将询问的要求 当前 mid 的数量,然后归到第一类。

将修改按照值也分为两类,同一类中的询问和修改的顺序要保持原样

撤销刚才的线段树操作,递归做即可。

一定要点开来看的代码,不然大概率听不懂
//We'll be counting stars.
#include<bits/stdc++.h>
using namespace std;
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define int long long
char buf[1<<21],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read() {
int x=0,f=1;
char c=gc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=gc();}
return x*f;
}
#define N 50002
struct node{ int op,l,r,c,ans; }q[N];
int n,m,a[N],a1[N],a2[N],t1,t2,t[N<<2],lz[N<<2];
#define mid ((l+r)>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
void tag(int rt,int l,int r,int val){
t[rt]+=(r-l+1)*val;
lz[rt]+=val;
}
void pd(int rt,int l,int r){
if(lz[rt]){
tag(ls,l,mid,lz[rt]);
tag(rs,mid+1,r,lz[rt]);
lz[rt]=0;
}
}
void add(int rt,int l,int r,int x,int y,int val){
if(x<=l && r<=y){
tag(rt,l,r,val);
return ;
}
pd(rt,l,r);
if(x<=mid) add(ls,l,mid,x,y,val);
if(y>mid) add(rs,mid+1,r,x,y,val);
t[rt]=t[ls]+t[rs];
}
int que(int rt,int l,int r,int x,int y){
if(x<=l && r<=y) return t[rt];
pd(rt,l,r);
int res=0;
if(x<=mid) res+=que(ls,l,mid,x,y);
if(y>mid) res+=que(rs,mid+1,r,x,y);
return res;
}
void solve(int l,int r,int L,int R){
if(L>R || l>r) return ;
t1=t2=0;
For(i,L,R){
if(q[a[i]].op==1){
if(q[a[i]].c>=mid){
a2[++t2]=a[i];
add(1,1,n,q[a[i]].l,q[a[i]].r,1);
}else{
a1[++t1]=a[i];
}
}else{
int tmp=que(1,1,n,q[a[i]].l,q[a[i]].r);
if(tmp>=q[a[i]].c){
q[a[i]].ans=mid;
a2[++t2]=a[i];
}else{
q[a[i]].c-=tmp;
a1[++t1]=a[i];
}
}
}
For(i,L,R){
if(q[a[i]].op==1){
if(q[a[i]].c>=mid){
add(1,1,n,q[a[i]].l,q[a[i]].r,-1);
}
}
}
For(i,1,t1) a[L+i-1]=a1[i];
For(i,1,t2) a[L+t1+i-1]=a2[i];
int gap=L+t1;
solve(l,mid-1,L,gap-1),solve(mid+1,r,gap,R);
}
signed main(){
n=read(),m=read();
For(i,1,m) q[i]=(node){read(),read(),read(),read(),0};
iota(a+1,a+1+m,1);
solve(-n,n,1,m);
For(i,1,m) if(q[i].op==2) printf("%lld\n",q[i].ans);
return 0;}

本题的注意事项:

由于有类似于偏序的关系,我们只能将整体二分写成削弱询问的形式:

void solve(l,r,part){
decrease the queries to the left part while splitting into two parts by value mid
revoke the operations have just done
solve(l,mid-1,leftpart)
solve(mid+1,r,rightpart)
}

而不能写成撤销的形式:

void solve(l,r,part){
split into two parts by value mid
solve(l,mid-1,leftpart)
revoke
solve(mid+1,r,rightpart)
}

(所以说整体二分没有固定的写法,因题而异)

P3834 【模板】可持久化线段树 2

我还是用常数小的 O(nlog2n) 整体二分艹过去了,抱歉(

点击查看代码
//#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
#define fir first
#define sec second
#define mkp make_pair
#define pb emplace_back
#define For(i,j,k) for(int i=(j),i##_=(k);i<=i##_;i++)
#define Rof(i,j,k) for(int i=(j),i##_=(k);i>=i##_;i--)
#define ckmx(a,b) a=max(a,b)
#define ckmn(a,b) a=min(a,b)
#define debug(...) cerr<<"#"<<__LINE__<<": "<<__VA_ARGS__<<endl
#define ll long long
const ll mod=1;
inline ll pw(ll x,ll y){ll r=1;while(y){if(y&1)r=r*x%mod;x=x*x%mod;y>>=1;}return r;}
#define int ll
//global erfen
#define N 200010
#define low (x&(-x))
struct Que{
int l,r,k;
}q[N];
int b[N],a[N],c[N],n,m,lim,s[N],s0[N],s1[N],t0,t1,ans[N];
vector<int> p[N];
void add(int x,int y){
while(x<=n){
c[x]+=y;
x+=low;
}
}
int que(int x){
int res=0;
while(x){
res+=c[x];
x-=low;
}
return res;
}
void work(int val,int opt){ for(int i:p[val]) add(i,opt); }
bool calc(Que x){ return que(x.r)-que(x.l-1)>=x.k; }
void solve(int l,int r,int L,int R){
if(l>r || L>R) return ;
int mid=(l+r)>>1;
For(i,l,mid) work(i,1);
t0=t1=0;
For(i,L,R){
if(calc(q[s[i]])){
ans[s[i]]=mid;
s0[++t0]=s[i];
}else{
s1[++t1]=s[i];
}
}
int tmp=L+t0-1;
For(i,1,t0) s[L-1+i]=s0[i];
For(i,1,t1) s[tmp+i]=s1[i];
solve(mid+1,r,tmp+1,R);
For(i,l,mid) work(i,-1);
solve(l,mid-1,L,tmp);
}
signed main(){ios::sync_with_stdio(false),cin.tie(nullptr);
cin>>n>>m;
For(i,1,n) cin>>a[i];
copy(a+1,a+1+n,b+1);
sort(b+1,b+1+n);
lim=unique(b+1,b+1+n)-b-1;
For(i,1,n) a[i]=lower_bound(b+1,b+1+lim,a[i])-b;
For(i,1,n) p[a[i]].pb(i);
For(i,1,m) cin>>q[i].l>>q[i].r>>q[i].k;
iota(s+1,s+1+m,1);
fill(ans+1,ans+1+m,lim+1);
solve(1,lim,1,m);
For(i,1,m) assert(ans[i]<=lim);
For(i,1,m) cout<<b[ans[i]]<<endl;
return 0;}
posted @   ShaoJia  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示