专栏文章

题解:P3374 【模板】树状数组 1

P3374题解参与者 5已保存评论 5

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
5 条
当前快照
1 份
快照标识符
@mipjwm9t
此快照首次捕获于
2025/12/03 13:13
3 个月前
此快照最后确认于
2025/12/03 13:13
3 个月前
查看原文

二维偏序大意

给定一个序列 a,ba, b,对每个 ii,求出所有 jj,使得 aia _ {i} \leq aja _ {j} bib _ {i} \leq bjb _ {j}

如何做哪?

  1. 排序预处理
    序列的顺序不会影响答案,所以先排序(以 aa 作为第一关键字,bb 作为第二关键字)。
  2. 归并框架
    所有符合条件的 jj 一定在 ii 之前(因为只有 ii 前的 jj 才满足 aia _ {i} \leq aja _ {j} ,并在 aia _ {i} == aia _ {i} bib _ {i} \leq bjb _ {j} )。
    对序列进行归并排序,分治时存在三种情况:
    • 情况 1i,ji, j 都在左边 → 递归左半部分 。
    • 情况 2i,ji, j 都在右边 → 递归右半部分 。
    • 情况 3jj 在左,ii 在右 → 左半部分对右半部分产生贡献 (步骤与归并排序求逆序对类似) 。

本题解法

  • 将所有操作(包括初始序列)视为一个序列:
    • 初始序列和添加操作 \to 操作 1
    • 查询操作拆分为两个子操作:
  • [1,x1][1, x-1] 的前缀和 \to 操作 2
  • [1,y][1, y] 的前缀和 \to 操作 3
  • 排序规则:
    • 第一关键字:pospos(操作下标) 。
    • 第二关键字:typetype(操作类型,操作 1 在前) 。

代码实现

CPP
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;

struct GG{
    int type,pos,v; // type:1-修改 2-减查询 3-加查询
    bool operator <(const GG &t)const{
        if(pos==t.pos) return type<t.type;
        return pos<t.pos;
    }
}q[N],tmp[N];

int n,m,idx=0,qidx=0,ans[N];

void CDQ(int l,int r){
    if(l==r) return ;
    int mid=(l+r)>>1;
    CDQ(l,mid),CDQ(mid+1,r);
    int k=0,i=l,j=mid+1,sum=0;
    while(i<=mid&&j<=r){
        if(q[i]<q[j]){
            if(q[i].type==1) sum+=q[i].v;
            tmp[k++]=q[i++];
        }else{
            if(q[j].type==2) ans[q[j].v]-=sum;
            if(q[j].type==3) ans[q[j].v]+=sum;
            tmp[k++]=q[j++];
        }
    }
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r){
        if(q[j].type==2) ans[q[j].v]-=sum;
        if(q[j].type==3) ans[q[j].v]+=sum;
        tmp[k++]=q[j++];
    }
    for(int i=0,j=l;j<=r;i++,j++) q[j]=tmp[i];
    return ;
}

int main(){
    std::ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        q[++idx]={1,i,x};
    }
    while(m--){
        int op,x,y;
        cin>>op>>x>>y;
        if(op==1){
            q[++idx]={1,x,y};
        }else{
            qidx++;
            q[++idx]={2,x-1,qidx}; // 前缀减查询
            q[++idx]={3,y,qidx}; // 前缀加查询
        }
    }
    CDQ(1,idx);
    for(int i=1;i<=qidx;i++) cout<<ans[i]<<endl;
    return 0;
}

评论

5 条评论,欢迎与作者交流。

正在加载评论...