洛谷 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\n",ans[i]);
    return 0;
}

 

posted @ 2018-10-13 11:16  hinata_hajime  阅读(475)  评论(0编辑  收藏  举报