• 三维偏序(陌上花开)


    三维偏序(陌上花开)

    题目背景

    这是一道模板题

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

    题目描述

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

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

    输入输出格式

    输入格式:

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

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

    输出格式:

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

    输入输出样例

    输入样例: 
    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
    输出样例: 
    3
    1
    3
    0
    1
    0
    1
    0
    0
    1

    说明

    1n100000,1k200000

    题目链接:https://www.luogu.org/problemnew/show/P3810


    CDQ分治,首先按对原序列进行三维排序,然后运用分治的思想,分治过程向下的时候保证a有序,分治过程回溯的时候保证b有序,某一分治中间态,我们只考虑mid左边对mid右边的影响(同在左边或者同在右边通过分治解决),因为mid左边的a一定小于等于mid右边的a,然后我们按照归并排序的方法使得b也有序,这样对于mid右边的数来说,当前中间态有且仅有左边的且b小于等于该数的数才能对它有贡献,于是可以维护一个树状数组,在对b进行归并排序的过程中,统计左边的c出现的次数,并计算左边的数对右边的数的贡献。

    小细节:若两个数的a,b,c值都相同,那么如果直接进行CDQ分治会导致相同数的贡献不能全部计算的情况,因为CDQ分治保证了每次计算左边的数对右边的数的贡献,但是相同的数无论左右都对彼此有贡献。所以要先去重,然后用一个权值代表该数出现了多少次。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 2e5+50;
    struct ss
    {
        int a,b,c,value,ans;
    
        bool operator < (const ss &s)const
        {
            if(a!=s.a)return a<s.a;
            if(b!=s.b)return b<s.b;
            return c<s.c;
        }
    
        bool operator == (const ss &s)const
        {
            return (a==s.a&&b==s.b&&c==s.c);
        }
    
    };
    
    ss arr[N],ls[N];
    pair<int,int> ls2[N];
    
    int c[N];
    void update(int pos,int v)
    {
        for(int i=pos; i<N; i+=i&(-i))c[i]+=v;
    }
    int Sum(int pos)
    {
        int ans=0;
        for(int i=pos; i>0; i-=i&(-i))ans+=c[i];
        return ans;
    }
    
    void print(int l,int r)
    {
        printf("%d %d:
    ",l,r);
        for(int i=l; i<=r; i++)printf("%d %d %d %d
    ",arr[i].a,arr[i].b,arr[i].c,arr[i].ans);
        printf("
    ");
    }
    
    void cdq(int l,int r) //分治 
    {
        if(l==r)return;
        int mid=(l+r)/2;
        cdq(l,mid);    //先解决子问题 
        cdq(mid+1,r);
    
        int sum_ls2=0;
    
        int i=l,j=mid+1,k=l;
    
    //print(l,r);
    
        while(i<=mid&&j<=r)
        {
            if(arr[i].b<=arr[j].b)//如果左边的数的b比较小,就更新树状数组,以便后边统计答案 
            {
                update(arr[i].c,arr[i].value);
                ls2[sum_ls2++]= {pair<int,int>(arr[i].c,arr[i].value)};
                ls[k++]=arr[i++];
            }
            else//右边的数的b值比较小,那么此时左边的剩余的数的b值一定都大于该数的b值,则此时对该数有贡献的- 
            {   //-数一定都是前面加到树状数组里的数 
                arr[j].ans+=Sum(arr[j].c);
                ls[k++]=arr[j++];
            }
        }
    
        while(i<=mid)
        {
            ls[k++]=arr[i++];
        }
    
        while(j<=r)
        {
            arr[j].ans+=Sum(arr[j].c);
            ls[k++]=arr[j++];
        }
    
        for(int i=0; i<sum_ls2; i++)update(ls2[i].first,-ls2[i].second);//不能memset清空树状数组 
        for(int i=l; i<=r; i++)arr[i]=ls[i];
    
        //print(l,r);
    
    }
    
    int ans[N]= {0};
    int main()
    {
        int n,k;
        scanf("%d %d",&n,&k);
        for(int i=1; i<=n; i++)scanf("%d %d %d",&arr[i].a,&arr[i].b,&arr[i].c),arr[i].ans=0;
    
        sort(arr+1,arr+1+n);
        int pos=1;
        arr[1].value=1;
    
        for(int i=2; i<=n; i++)  //去重 
        {
            if(arr[i]==arr[i-1])
            {
                arr[pos].value++;
            }
            else
            {
                arr[++pos]=arr[i];
                arr[pos].value=1;
            }
        }
    
    //    for(int i=1;i<=pos;i++)printf("%d %d %d %d
    ",arr[i].a,arr[i].b,arr[i].c,arr[i].value);
    
    
        cdq(1,pos);
        for(int i=1; i<=pos; i++)
        {
            arr[i].ans+=arr[i].value-1;  //加上相同数的贡献 
            ans[arr[i].ans]+=arr[i].value;
        }
    
        for(int i=0; i<n; i++)printf("%d
    ",ans[i]);
        return 0;
    
    }
    View Code
  • 相关阅读:
    向内的寻找
    了解潜意识
    NOI2014 Day1
    NOI2011 Day1
    NOI2012 Day2
    NOI2012 Day1
    NOI2013 Day2
    NOI2013 Day1
    拉格朗日乘数法
    NOI2015 Day2
  • 原文地址:https://www.cnblogs.com/tian-luo/p/11211679.html
Copyright © 2020-2023  润新知