zoukankan      html  css  js  c++  java
  • 三维偏序(陌上花开)

    三维偏序(陌上花开)

    有n个元素,第i个元素有(a_i)(b_i)(c_i)三个属性,设(f(i))表示满足(a_jle a_i)(b_jle b_i)(c_jle c_i)的j的数量。对于(din[0, n)),求(f(i)=d)的数量。

    偏序关系,意思是并不是任意两个元素之间都有关系。以前在机房里天天听到“偏序”“cdq”,也知道偏序问题要用cdq分治来做。现在终于会辣!

    显然,我们处理的是三元组之间的关系,并不关心三元组的值和位置。所以把第一维排序是很自然的想法。第一维一排序,我们就发现,三维偏序被转化成了一个带限制的二维偏序问题。由(b_i)(c_i)组成的二元组,它们的偏序关系和位置关系必须相同。这导致不能再排序第二维了。

    考虑如何解除偏序关系和位置关系相同的限制。啊哈!cdq分治就是用来解除求解问题时位置的限制的,因为它能让我们只考虑左区间对于右区间的影响/贡献。对于三维偏序来说:元素对a值排完序以后,把元素分成左右两个区间,那么左边区间的a值还是小于右边区间的a值。因此可以对带限制的二维偏序问题cdq分治,这个问题在某个区间([l, r])下就被转化为了一个元素为((b_i, c_i))的二维偏序问题(注意两个二元组能相比较的前提是在两个不同的区间里,不然不能保证第一维重排以后还有序)!用树状数组,或者再用cdq去解决这个([l,r])区间内的二维偏序问题即可。

    我们发现,不仅仅是对三维偏序,对任意维偏序都可以用这个方法,也就是cdq中不停套cdq。除去排序的那一维,可以发现n维偏序的时间复杂度就是(O(Llog^{n-1}L))。当n=1时结论也成立(滑稽)

    话说,这道题的名字看着莫名的舒服,果然这就是古汉语的魅力啊。与日语中某些人名地名让我们感到诗意也是一个道理吧~

    // luogu-judger-enable-o2
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e5+5;
    struct node{
        int x, y, z, ans, w;  //w表示有几个重复的三元组
    }a[maxn], b[maxn];
    bool operator ==(node &a, node &b){ return a.x==b.x&&a.y==b.y&&a.z==b.z; }
    int n1, n, m, cnt[maxn];
    bool cmpx(node a, node b){
        if (a.x==b.x){
            if (a.y==b.y) return a.z<b.z;
            return a.y<b.y;
        } return a.x<b.x;
    }
    bool cmpy(node a, node b){
        if (a.y==b.y) return a.z<b.z;
        return a.y<b.y;
    }
    
    struct Ta{
        int a[maxn*2];
        inline int lowbit(int x){ return x&(-x); }
        int query(int x){
            int ans=0;
            for (; x; x-=lowbit(x)) ans+=a[x];
            return ans; }
        void add(int x, int k){
            for (; x<=m; x+=lowbit(x)) a[x]+=k;
        }
    }ta;
    
    void cdq(int l, int r){
        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);
        int i=mid+1, j=l;
        for (; i<=r; ++i){  //现在转化为一个带L/R标记的二维偏序问题
            while (a[j].y<=a[i].y&&j<=mid)
                ta.add(a[j].z, a[j].w), ++j;
            a[i].ans+=ta.query(a[i].z);  //对于当前三元组,有多少个第二、三维小于它
        }
        for (i=l; i<j; ++i) ta.add(a[i].z, -a[i].w);
    }
    
    int main(){
        scanf("%d%d", &n1, &m);
        for (int i=1; i<=n1; ++i) scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].z);
        sort(b+1, b+n1+1, cmpx); int t=0;
        for (int i=1; i<=n1; ++i){
            ++t;  //t表示和之前的三元组完全相同的三元组数量
            if (!(b[i]==b[i+1])) a[++n]=b[i], a[n].w=t, t=0;
        }
        cdq(1, n);
        for (int i=1; i<=n; ++i)
            cnt[a[i].ans+a[i].w-1]+=a[i].w;
        for (int i=0; i<n1; ++i) printf("%d
    ", cnt[i]);
        return 0;
    }
    
  • 相关阅读:
    js 数组的length(javascript教程四)
    js利用数组length属性清空和截短数组
    mysql一对多关联查询的时候筛选条件
    PHP错误处理及异常处理笔记
    Javascript获取URL地址变量参数值的方法
    php 提示Warning: mysql_fetch_array() expects
    Centos中安装PHP的PDO MySQL扩展的教程
    ASP.NET缓存 Cache之数据缓存
    Spring.Net框架一:Spring.Net简介
    T4模板
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9297296.html
Copyright © 2011-2022 走看看