一般情况下,我们比较一个数大小,就是ai>aj即可,

而在上升子序列中,当i>j并且ai>aj的时候,才可以认为i这位的数大于j这位的数。

这就是一个二维偏序。

 

类似的,有n个数,每个数m个属性,一个数比另一个数大,当且仅当这个数的所有属性都大于另一个数。

这就是一个m维偏序。

 

对于三维偏序,可以用cdq分治、排序、树状数组处理。

luogu P3810

Description:

有 n 个元素,第 i 个元素有 ai 、bi 、ci 三个属性,设 f(i) 表示满足 ajai 且bjbi 且cjci 的 j 的数量。

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

Solution:

三维偏序的模板题。

我们先按照a从小到大排序,

然后cdq分治。

先递归到两边,

回溯到这一层之后,把左儿子的所有的数按照b排序,右儿子的所有数也按照b排序。

这样,左儿子的数之间虽然a不一定递增,但是因为开始按照a排序,所以左边所有的数的a一定都小于右边的数。

b排好序之后,

两个指针j,i分别从1~mid,mid+1~r 即左右儿子区间的起止点开始走,,

对于右边的一个数i,当j的数的b值不大于i的b值时,不断向后走j,并且把这些数的c值放进一个权值树状数组里,

当j的b值大于i之后,当前所有左儿子里面,b小于i这个数的数的c属性都放进树状数组里了。

只有放进去的这些数才可能来更新f值。

在i向后走之前,f[a[i].ans]+=query(a[i].z),满足第三个条件的数也找到了。

就把所有当前这个层里面,符合条件的数都找出来了。

最后,把树状数组加上的 1都消去。

 

由于cdq分治,会把i之前的所有的数都分成logn个区间,更新完f[i]了。

大家可以手动画图,或者模拟一下。

 

复杂度:nlogn^2

 

那么,这个算法是怎么样实现三维偏序的处理呢?

1.对于a,开始直接排序,并且,每次是先递归左右儿子,再处理这一层,

所以保证一个数pi前面的所有的数,不论之后怎么换位置,都不会到i的后面。

这就利用位置保证了所有可能更新f[i]的数对于a都是合法的。

2.对于b,我们每次回溯的时候,按照b排了一个序,

对于左子区间对右子区间的影响,通过指针,把b小于等于i的数的所有数的c放进了树状数组里。

这样,i前面的logn个区间,会把所有b小于i的b的数都考虑一遍的。也合法。

3.对于c,直接通过树状数组前缀和,一步就求出来了当前合法的所有数了。

相当于一个筛,留下a合法的,留下b合法的,最后能被c留下的,就是所有合法的了。

 

注意因为是小于等于号,所以我们先把所有的完全相同的数合并成一个数,统计的时候,一个数也大于等于自己。再加上就好了。

就是细节问题。

 

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=100000+10;
int n,m;
int f[N];
struct node{
    int x,y,z;
    int ans;
    int w;
}a[N],b[N];
int pp;
bool cmpx(node p,node q){
    if(q.x==p.x){
        if(p.y==q.y) return p.z<q.z;
        return p.y<q.y;
    }
    return p.x<q.x;
}
bool cmpy(node p,node q){
    if(q.y==p.y){
        return p.z<q.z;
    }
    return p.y<q.y;
}
struct ta{
    int g[2*N];
    void add(int x,int k){
        for(;x<=pp;x+=x&(-x)) g[x]+=k;
    }
    int ask(int x){
        int ret=0;
        for(;x;x-=x&(-x)) ret+=g[x];return ret;
    }
}t;

void cdq(int l,int r){
    //cout<<l<<" and "<<r<<endl;
    if(l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    sort(a+l,a+mid+1,cmpy);
    sort(a+mid+1,a+r+1,cmpy);
    //cout<<" after sort "<<l<<" || "<<r<<endl;
    int i=mid+1,j=l;
    for(;i<=r;i++){
        while(a[j].y<=a[i].y&&j<=mid){
            t.add(a[j].z,a[j].w),j++;
        }
        a[i].ans+=t.ask(a[i].z);
    }
    for(i=l;i<j;i++){
        t.add(a[i].z,-a[i].w);
    }
}
int ans[N];
int main()
{    
    int m;
    scanf("%d%d",&m,&pp);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&b[i].x,&b[i].y,&b[i].z);
    }
    sort(b+1,b+m+1,cmpx);
    int c=0;
    for(int i=1;i<=m;i++){
        c++;
        if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z){
            a[++n]=b[i],a[n].w=c;c=0;
        }
    }
    cdq(1,n);
    for(int i=1;i<=n;i++){
        ans[a[i].ans+a[i].w-1]+=a[i].w;
    }
    for(int i=0;i<m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
三维偏序

相关文章:

  • 2021-10-28
  • 2022-12-23
  • 2021-07-23
  • 2022-12-23
  • 2022-12-23
  • 2022-02-02
  • 2022-02-20
猜你喜欢
  • 2021-11-06
  • 2021-07-19
  • 2021-08-18
  • 2022-12-23
  • 2022-12-23
  • 2021-10-24
相关资源
相似解决方案