三维偏序

P3810 【模板】三维偏序(陌上花开)

题目背景

这是一道模板题

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

题目描述

\(n\)个元素,第\(i\)个元素有\(a_i\)\(b_i\)\(c_i\)三个属性,设\(f(i)\)表示满足\(a_j \leq a_i\)\(b_j \leq b_i\)\(c_j \leq c_i\)\(j\)的数量。

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

输入输出格式

输入格式:

第一行两个整数\(n\)\(k\),分别表示元素数量和最大属性值。

之后\(n\)行,每行三个整数\(a_i\)\(b_i\)\(c_i\),分别表示三个属性值。

输出格式:

输出\(n\)行,第\(d+1\)行表示\(f(i)=d\)\(i\)的数量。

说明

$1 \leq n \leq 100000, 1 \leq k \leq 200000 $


这里采用的是归并排序套树状数组的做法。

先不考虑重复元素

首先考虑二维偏序,我们可以对二元组\((x,y)\)\(x\)为第一关键字,\(y\)为第二关键字进行排序。然后单独讨论\(y\)的顺序对(与逆序对相反的一个概念),每个\(y\)的顺序对数量即为满足题目条件的顺序对数量。这点不难想明白。

扫描二维码关注公众号,回复: 2168754 查看本文章

然后类比三维偏序,我们可以对三元组\((x,y,z)\)同样如此做,但统计时并不是\(y\)的所有顺序对都合法,我们对统计时的\(z\)维护一权值树状数组,每次查询在顺序对中有多少个\(z\)小于等于当前的\(z\)

考虑重复元素,这个题题意没有明确说明,但每个三元组是不考虑自己的。

我们先缩点去重,然后统计的时候按数量统计即可


Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=100010;
const int M=200010;
struct node
{
    int x,y,z,cnt;
    bool friend operator <(node n1,node n2)
    {
        if(n1.x==n2.x)
        {
            if(n1.y==n2.y) return n1.z<n2.z;
            return n1.y<=n2.y;
        }
        return n1.x<n2.x;
    }
}a[N],d[N],dx[N],tmp[N];
int n,n_,k;
void init()
{
    scanf("%d%d",&n_,&k);
    for(int i=1;i<=n_;i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        a[i].cnt=1;
    }
    sort(a+1,a+1+n_);
    for(int i=1;i<=n_;i++)
    {
        while(d[n].x==a[i].x&&d[n].y==a[i].y&&d[n].z==a[i].z) d[n].cnt++,i++;
        if(i<=n_) d[++n]=a[i];
    }
}
int sum[M],ans[N],cnt0[N];
void add(int x,int delta)
{
    while(x<=k)
    {
        sum[x]+=delta;
        x+=x&-x;
    }
}
int query(int x)
{
    int s=0;
    while(x)
    {
        s+=sum[x];
        x-=x&-x;
    }
    return s;
}
void divide(int l,int r)
{
    if(l==r) {cnt0[l]+=dx[l].cnt-1;return;}
    int mid=l+r>>1;
    divide(l,mid);
    divide(mid+1,r);
    int pos=l,cnt=l-1;
    for(int i=mid+1;i<=r;i++)
    {
        while(pos<=mid&&dx[pos]<dx[i])
        {
            tmp[++cnt]=dx[pos];
            add(dx[pos].y,dx[pos].cnt);
            pos++;
        }
        tmp[++cnt]=dx[i],cnt0[dx[i].z]+=query(dx[i].y);
    }
    for(int i=l;i<pos;i++)
        add(dx[i].y,-dx[i].cnt);
    while(pos<=mid) tmp[++cnt]=dx[pos++];
    for(int i=l;i<=r;i++)
        dx[i]=tmp[i];
}
void work()
{
    for(int i=1;i<=n;i++)
        dx[i].x=d[i].y,dx[i].y=d[i].z,dx[i].z=i,dx[i].cnt=d[i].cnt;
    divide(1,n);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=dx[i].cnt;j++)
            ans[cnt0[dx[i].z]]++;
    }
    for(int i=0;i<n_;i++)
        printf("%d\n",ans[i]);
}
int main()
{
    init();
    work();
    return 0;
}

2018.7.15

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9315447.html