偏序问题
P10814 【模板】离线二维数点
询问区间 \([l,r]\) 内,在值域为 \([0,x]\) 中数的个数。
想象这是个二维平面,我们要求的就是矩形内点的个数。
通常用 cdq 做,将询问拆成两个点,将点按照x坐标排序,按照y坐标加入到树状数组中(为了满足第二条限制),这时就依次加入点,并用树状数组进行前缀和相减,用两个前缀求区间信息,并且两个前缀都是在满足限制的情况下计算的。
P2163 [SHOI2007] 园丁的烦恼
同样二维数点题目,不过查询区间改为了矩形,我们同样用二维前缀和的做法,容斥掉多余的部分。
点击查看代码
#include <bits/stdc++.h> #define ll long long #define int ll #define ls p<<1 #define rs p<<1|1 #define re register #define pb push_back #define pir pair<int,int> #define f(a,x,i) for(int i=a;i<=x;i++) #define fr(a,x,i) for(int i=a;i>=x;i--) #define lb(x) x&(-x); using namespace std; const int N=5e5+10; const int B=1e6+5; const int M=8e6+10; const int mod=1e9+7; int n,m; struct ss{ int x,y,id,op,w; }a[M];//点坐标 struct sss{ int x,y,z,h; }q[N]; int ans[N];//答案 int cnt=1; int b[M],tot=0;//离散化 int len=0; int xx[N],yy[N]; bool cmp(ss g,ss h){ if(g.x!=h.x) return g.x<h.x; if(g.y!=h.y) return g.y<h.y; return g.w>h.w; } int t[N+10]; void add(int x,int k){ while(x<=len+10){ t[x]+=k,x+=lb(x); } } int query(int x){ int sum=0; while(x){ sum+=t[x],x-=lb(x); } return sum; } void solve(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); cin>>n>>m; for(int i=1;i<=n;i++){ int x,y; cin>>x>>y; xx[i]=x;yy[i]=y; b[++tot]=x;b[++tot]=y; } for(int i=1;i<=m;i++){ int x,y,z,h; cin>>x>>y>>z>>h; q[i]={x,y,z,h}; b[++tot]=x;b[++tot]=y;b[++tot]=z;b[++tot]=h; } sort(b+1,b+1+tot); len=unique(b+1,b+1+tot)-b-1; for(int i=1;i<=n;i++){ int x,y; x=lower_bound(b+1,b+1+len,xx[i])-b+2; y=lower_bound(b+1,b+1+len,yy[i])-b+2; a[cnt++]={x,y,0,1,1}; } for(int i=1;i<=m;i++){ int x,y,z,h; x=lower_bound(b+1,b+1+len,q[i].x)-b+2; y=lower_bound(b+1,b+1+len,q[i].y)-b+2; z=lower_bound(b+1,b+1+len,q[i].z)-b+2; h=lower_bound(b+1,b+1+len,q[i].h)-b+2; a[cnt++]={x-1,y-1,i,1,0}; a[cnt++]={x-1,h,i,-1,0}; a[cnt++]={z,y-1,i,-1,0}; a[cnt++]={z,h,i,1,0}; } cnt--; sort(a+1,a+cnt+1,cmp); for(int i=1;i<=cnt;i++){ add(a[i].y,a[i].w); if(a[i].w==0){ int sum=a[i].op*query(a[i].y); ans[a[i].id]+=sum; } } for(int i=1;i<=m;i++){ cout<<ans[i]<<"\n"; } } signed main(){ ios::sync_with_stdio(0); cin.tie(nullptr); int t=1; // cin>>t; while(t--){ solve(); } return 0; }
P3755 [CQOI2017] 老C的任务
上一题的扩展,每个点带权值,改改就行。
P4390 [BalkanOI2007] Mokia 摩基亚
多了个修改操作,但不是强制在线,这题同时限制了修改时间、纵坐标、横坐标,是个三维偏序问题,cdq分治解决了,我们只要按操作依次加点就是已经满足了时间有序。
点击查看代码
#include <bits/stdc++.h> #define ll long long #define int ll #define ls p<<1 #define rs p<<1|1 #define re register #define pb push_back #define pir pair<int,int> #define f(a,x,i) for(int i=a;i<=x;i++) #define fr(a,x,i) for(int i=a;i>=x;i--) #define lb(x) x&(-x); using namespace std; const int N=2e6+10; const int B=1e6+5; const int M=8e6+10; const int mod=1e9+7; int n,m; struct ss{ int x,y,id,op,w; }a[M];//点坐标 struct sss{ int x,y,z,h; }q[N]; int ans[N];//答案 int cnt=1; bool cmp(ss g,ss h){ if(g.x!=h.x) return g.x<h.x; if(g.y!=h.y) return g.y<h.y; return g.w>h.w; } int t[N+10]; void add(int x,int k){ while(x<=2e6+10){ t[x]+=k,x+=lb(x); } } int query(int x){ int sum=0; while(x){ sum+=t[x],x-=lb(x); } return sum; } void cdq(int l,int r){ if(l==r) return; int mid=(l+r)>>1; cdq(l,mid),cdq(mid+1,r); sort(a+l,a+mid+1,cmp); sort(a+mid+1,a+r+1,cmp); int i=l,j=mid+1; for(;j<=r;j++){ while(i<=mid&&a[i].x<=a[j].x){ if(a[i].w!=0){ add(a[i].y,a[i].w); } i++; } if(a[j].w==0){ ans[a[j].id]+=a[j].op*query(a[j].y); } } for(j=l;j<i;j++){ if(a[j].w!=0){ add(a[j].y,-a[j].w); } } } void solve(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); cin>>m>>n; m=0,n=0; int op=0; cin>>op; while(op!=3){ if(op==1){ int x,y,w; cin>>x>>y>>w; a[cnt++]={x,y,0,1,w}; } else{ int x,y,z,h; cin>>x>>y>>z>>h; m++; a[cnt++]={x-1,y-1,m,1,0}; a[cnt++]={x-1,h,m,-1,0}; a[cnt++]={z,y-1,m,-1,0}; a[cnt++]={z,h,m,1,0}; } cin>>op; } cnt--; cdq(1,cnt); for(int i=1;i<=m;i++){ cout<<ans[i]<<"\n"; } } signed main(){ ios::sync_with_stdio(0); cin.tie(nullptr); int t=1; // cin>>t; while(t--){ solve(); } return 0; }
P3157 [CQOI2011] 动态逆序对
单独算贡献,删掉一个数会影响这个数之前比它大的数和之后比它小的数,cdq离线单独求出每次操作所影响的贡献。
#include <bits/stdc++.h> #define ll long long #define int ll #define ls p<<1 #define rs p<<1|1 #define re register #define pb push_back #define pir pair<int,int> #define f(a,x,i) for(int i=a;i<=x;i++) #define fr(a,x,i) for(int i=a;i>=x;i--) #define lb(x) x&(-x); using namespace std; const int N=1e6+10; const int B=1e6+5; const int M=8e6+10; const int mod=1e9+7; int n,m; struct ss{ int x,id,op,pos,tim; }a[M]; map<int,int> mp; int ans[N];//答案 int cnt; bool cmp(ss g,ss h){ return g.pos<h.pos; } int t[N+10]; void add(int x,int k){ while(x<=1e6+10){ t[x]+=k,x+=lb(x); } } int query(int x){ int sum=0; while(x){ sum+=t[x],x-=lb(x); } return sum; } void cdq(int l,int r){ if(l==r) return; int mid=(l+r)>>1; cdq(l,mid),cdq(mid+1,r); sort(a+l,a+mid+1,cmp); sort(a+mid+1,a+r+1,cmp); int i=l,j=mid+1; for(;j<=r;j++){ while(i<=mid&&a[i].pos<=a[j].pos){ add(a[i].x,a[i].op); i++; } ans[a[j].id]+=a[j].op*(query(n)-query(a[j].x)); } for(j=l;j<i;j++){ add(a[j].x,-a[j].op); } j=mid; for(i=r;i>mid;i--){ while(j>=l&&a[j].pos>=a[i].pos){ add(a[j].x,a[j].op); j--; } ans[a[i].id]+=a[i].op*query(a[i].x-1); } for(i=mid;i>j;i--){ add(a[i].x,-a[i].op); } } void solve(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); cin>>n>>m; for(int i=1;i<=n;i++){ int x; cin>>x; mp[x]=i; a[++cnt]={x,0,1,i,cnt}; } for(int i=1;i<=m;i++){ int x; cin>>x; a[++cnt]={x,i,-1,mp[x],cnt}; } cdq(1,cnt); for (int i=1;i<=m;++i){ ans[i]+=ans[i-1]; } for(int i=0;i<m;i++){ cout<<ans[i]<<"\n"; } } signed main(){ ios::sync_with_stdio(0); cin.tie(nullptr); int t=1; // cin>>t; while(t--){ solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具