• 洛谷 P3810 三位偏序(CDQ分治+树状数组)


    题目背景

    这是一道模板题

    可以使用bitset,CDQ分治,K-DTree等方式解决。

    题目描述

    有 nn 个元素,第 ii 个元素有 a_iaib_ibic_ici 三个属性,设 f(i)f(i) 表示满足 a_j leq a_iajai 且 b_j leq b_ibjbi 且 c_j leq c_icjci 的 jj 的数量。

    对于 d in [0, n)d[0,n),求 f(i) = df(i)=d 的数量

    输入输出格式

    输入格式:

    第一行两个整数 nn、kk,分别表示元素数量和最大属性值。

    之后 nn 行,每行三个整数 a_iaib_ibic_ici,分别表示三个属性值。

    输出格式:

    输出 nn 行,第 d + 1d+1 行表示 f(i) = df(i)=d 的 ii 的数量。

    输入输出样例

    输入样例#1: 复制
    10 3
    3 3 3
    2 3 3
    2 3 1
    3 1 1
    3 1 2
    1 3 1
    1 1 2
    1 2 2
    1 3 2
    1 2 1
    输出样例#1: 复制
    3
    1
    3
    0
    1
    0
    1
    0
    0
    1

    说明

    1 leq n leq 100000, 1 leq k leq 2000001n100000,1k200000

    分析:

    对于第一维的处理,用sort进行按a的大小排序,第二维的处理用CDQ分治,将总的区间二分成子区间,再将每个子区间分成左右两部分,再按B的大小排序,就能找到满足第二维的位置。第三维用树状数组进行处理,处理方法类似于树状数组对于逆序对的人处理,我们将c作为数组的下标加入到树状数组中,那么最后再统计sum(c)就是所有小于等于它的值的数量的总和。

    需要注意的是,由于题目中找的是大于等于这样的关系,所以排序之后,可能前面的数等于后面的数,造成前面的数的贡献的不完全统计,所以我们让a,b,c都相等的点的值,都等于它们之中的最大值

    这里用两个for进行最大值处理即可,具体见代码

    代码如下:

    // luogu-judger-enable-o2
    //查询和修改都是log(n)
    //从c[1]开始进行赋值,不赋c[0],n为数的个数
    //sum(x)为计算前x个数的和
    
    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int ans[100010];
    int n,k;
    struct Bitree
    {
        int c[200010];
        int lowbit(int x)
        {
            return x&(-x);
        }
        void add(int i,int value)
        {
    
            while(i<=k)
            {
                c[i]+=value;
                i+=lowbit(i);
            }
        }
        int  sum(int i)
        {
            int sum=0;
            while(i>=1)
            {
              sum+=c[i];
              i-=lowbit(i);
            }
            return sum;
        }
    }BIT;
    
    struct node
    {
        int a;
        int b;
        int c;
        int id;
        int sum;
    }poi[100010];
    
    bool cmpb(node x,node y)
    {
      if(x.b!=y.b)return x.b<y.b;
      return x.id<y.id;
    }
    
    bool cmp(node x,node y)
    {
        if(x.a!=y.a)return x.a<y.a;
        if(x.b!=y.b)return x.b<y.b;
        if(x.c!=y.c)return x.c<y.c;
    }
    
    bool cmp2(node x,node y)
    {
        if(x.a!=y.a)return x.a<y.a;
        if(x.b!=y.b)return x.b<y.b;
        if(x.c!=y.c)return x.c<y.c;
    }
    void CDQ(int l,int r)
    {
       if(l==r)return;
       int mid=(l+r)>>1;
       CDQ(l,mid);
       CDQ(mid+1,r);
       sort(poi+l,poi+mid+1,cmpb);
       sort(poi+mid+1,poi+r+1,cmpb);
       int j=l;
       for(int i=mid+1;i<=r;i++)
        {
          for(;j<=mid&&poi[i].b>=poi[j].b;j++)BIT.add(poi[j].c,1);
       //   cout<<l<<" "<<r<<" "<<i<<" "<<BIT.sum(poi[i].c)<<endl;
          poi[i].sum+=BIT.sum(poi[i].c);
        }
         for(int k=l;k<j;k++)
         BIT.add(poi[k].c,-1);
    }
    
    
    
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
       {
        scanf("%d%d%d",&poi[i].a,&poi[i].b,&poi[i].c);
        poi[i].sum=0;
       }
        sort(poi+1,poi+n+1,cmp);
        CDQ(1,n);
        sort(poi+1,poi+n+1,cmp2);
        for(int i=n-1;i>=1;i--)
        {
            if(poi[i+1].a==poi[i].a&&poi[i+1].b==poi[i].b&&poi[i+1].c==poi[i].c)
            poi[i].sum=max(poi[i].sum,poi[i+1].sum);
        }
    
        for(int i=2;i<=n;i++)
        {
            if(poi[i].a==poi[i-1].a&&poi[i].b==poi[i-1].b&&poi[i].c==poi[i-1].c)
            poi[i].sum=max(poi[i].sum,poi[i-1].sum);
        }
        for(int i=1;i<=n;i++)
        ans[poi[i].sum]++;
        for(int i=0;i<=n-1;i++)
        printf("%d
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    二叉树
    队列和栈
    时间复杂度和空间复杂度
    二分查找法
    排序算法值归并排序
    排序算法之选择排序类
    5.7.1.3 Global 对象的属性
    5.7.1.2 eval() 方法
    5.7.1.1 单体内置对象
    5.6.3.8 fromCharCode()方法
  • 原文地址:https://www.cnblogs.com/a249189046/p/9782248.html
Copyright © 2020-2023  润新知