偏序问题

P10814 【模板】离线二维数点

询问区间 \([l,r]\) 内,在值域为 \([0,x]\) 中数的个数。
想象这是个二维平面,我们要求的就是矩形内点的个数。

image

通常用 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;
}
posted @   sad_lin  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示