整体二分
个人感觉整体二分就像归并的逆操作。
P3527 [POI2011]MET-Meteors & LOJ2169. 「POI2011 R3 Day2」流星 Meteors
P1527 [国家集训队]矩阵乘法
P3332 [ZJOI2013]K大数查询
树套树 ,整体二分 (离线算法常数吊打树套树)。
我们每次可以定一个阈(yù)值 ,通过线段树判断每一个询问的答案是否 :
-
若是,则归到第二类。
-
若否,则将询问的要求 当前 的数量,然后归到第一类。
将修改按照值也分为两类,同一类中的询问和修改的顺序要保持原样。
撤销刚才的线段树操作,递归做即可。
一定要点开来看的代码,不然大概率听不懂
//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
我还是用常数小的 整体二分艹过去了,抱歉(
点击查看代码
//#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;}
本文来自博客园,作者:ShaoJia,版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义