题解-luogu P3810三维偏序(陌上花开)

\(\rm三维偏序\)

\(\rm一、定义:序列上每个点有三个权值~(x,y,z)~,求有多少个点对~(a,b)~同时满足~a_x \le b_x~,~a_y \le b_y~,~a_z \le b_z~\)

\(\rm二、解法:~cdq~分治\)

\(\rm\qquad首先,我们思考一下二维偏序的解法\)
\(\rm\qquad\qquad我们先对整个序列按照~x~排一遍序,那么我们就已经解决了一个维度\)
\(\rm\qquad\qquad即目前序列中所有后面的点的~x~值一定比前面的点小\)
\(\rm\qquad\qquad那么我们只需要考虑目前这个排序完的序列中有多少个顺序对\)(瞎jb造的词)
\(\rm\qquad\qquad具体而言,可以通过树状数组解决(归并排序也可,这里不赘述)\)
\(\rm\qquad\qquad对于第二权值值域开一颗树状数组,从左到右扫到~i~,查询已出现的数中比~a_i~小的数\)
\(\rm\qquad\qquad即树状数组上目前已有的~a_i~前的数的个数\)

\(\rm\qquad进入正题\)

\(\rm\qquad\qquad首先我们依旧可以通过排序降一个维,然后考虑分治\)
\(\rm\qquad\qquad对于区间~(l,r)~,我们只考虑跨~Mid~的贡献,区间内的分治到下面解决\)
\(\rm\qquad\qquad由于序列本来就是已经按照第一权值排过序的\)
\(\rm\qquad\qquad所以无论我们怎么搞事情,原本属于左半边的点的第一权值一定\le右半边的点\)(这不是废话吗)
\(\rm\qquad\qquad所以我们干脆可以直接再排一遍序来再降一维\)
\(\rm\qquad\qquad但是要注意只能考虑原本左半边的点与原本右半边的点的贡献\)
\(\rm\qquad\qquad所以我们可以通过记录原左边的点和原右边的点\)

\(\rm\qquad\qquad对于原左边的点,即时丢到树状数组里\)
\(\rm\qquad\qquad而对于原右边的点,我们就可以直接查询,就像二维偏序的做法一样\)

    bool cmp2(node a,node b){
        if(a.y!=b.y)return a.y<b.y;
        if(a.z!=b.z)return a.z<b.z;
        return a.x<b.x;
    }
    void solve(int l,int r){
        if(l==r)return;
        solve(l,Mid),solve(Mid+1,r);
        sort(a+l,a+r+1,cmp2);
        for(int i=l;i<=r;i++){
            if(a[i].x<=mid)add(a[i].z,1);
            else ANS[a[i].id]+=query(a[i].z);
        }
        for(int i=l;i<=r;i++)
            if(a[i].x<=mid)add(a[i].z,-1);
    }

\(\rm\qquad PS:如果愿意也可以手写一个归并排序然后即时操作,常数优一些\)

\(\rm\qquad另外要注意的是要去重,还有清空树状数组不能用~memset~(会超时)\)

\(\rm总代码如下:\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define Mid ((l+r)>>1)
#define int ll
const int maxn=300005,inf=0x3f3f3f3f,mod=1e9+7;
int n,m,Q,K,T;
int read(){
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    return x*f;
}
struct node{
    int x,y,z,id;//存储每个点的个权值
    bool operator <(const node &a)const{//按第一权值排序
        if(x!=a.x)return x<a.x;
        if(y!=a.y)return y<a.y;
        return z<a.z;
    }
}a[maxn],t[maxn];vector<int>q;//存储被修改的权值,实现更高效率的树状数组清空
int ANS[maxn],sum[maxn],f[maxn],fle[maxn];
int lowbit(int x){return x&-x;}
void add(int x,int k){//树状数组修改
    if(k>0)q.push_back(x);
    for(int i=x;i<=K;i+=lowbit(i)){//注意这里是K
        sum[i]+=k;
    }return;
}
int query(int x){//树状数组查询
    int ans=0;
    for(int i=x;i>=1;i-=lowbit(i)){
        ans+=sum[i];
    }return ans;
}
void solve(int l,int r){//分治
    if(l==r)return;
    solve(l,Mid),solve(Mid+1,r);
    int i=l,j=Mid+1,now=l;
    while(i<=Mid&&j<=r){//手写归并排序
        //各种分类讨论,可能是我写复杂了,要是您不屑的话也可以去看上文的简单写法
        if(a[i].y<a[j].y){add(a[i].z,1);t[now++]=a[i++];}
        else if(a[i].y>a[j].y){ANS[a[j].id]+=query(a[j].z);t[now++]=a[j++];}
        else{//注意无论如何每次只进一个元素,即不会有在一次循环中两个元素同时进入t数组的情况,否则会出错
            if(a[i].z<a[j].z)add(a[i].z,1),t[now++]=a[i++];
            else if(a[i].z>a[j].z)ANS[a[j].id]+=query(a[j].z),t[now++]=a[j++];
            else{
                if(a[i].x<a[j].x)add(a[i].z,1),t[now++]=a[i++];
                else ANS[a[j].id]+=query(a[j].z),t[now++]=a[j++];
            }
        }
    }
    while(i<=Mid)t[now++]=a[i++];
    while(j<=r)ANS[a[j].id]+=query(a[j].z),t[now++]=a[j++];
    for(int i=l;i<=r;++i)a[i]=t[i];
    for(int i=0;i<q.size();++i)add(q[i],-1);q.clear();//vector存储被修改的位置,实现更高效率的树状数组清空
    return;
}
signed main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    n=read();K=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read(),z=read();
        a[i].x=x,a[i].y=y,a[i].z=z;a[i].id=i;
    }sort(a+1,a+1+n);
    for(int i=1;i<=n;){//去重
        int j=i+1;
        while(j<=n&&a[j].x==a[i].x&&a[j].y==a[i].y&&a[j].z==a[i].z)j++;
        while(i<j)fle[a[i].id]=a[j-1].id,i++;
    }for(int i=1;i<=n;++i)a[i].x=i;
    solve(1,n);
    for(int i=1;i<=n;++i)f[ANS[fle[a[i].id]]]++;
    for(int i=0;i<n;++i)printf("%lld\n",f[i]);

    return 0;
}

原文地址:https://www.cnblogs.com/Zikual/p/12208149.html

时间: 2024-12-10 05:53:12

题解-luogu P3810三维偏序(陌上花开)的相关文章

P3810 -三维偏序(陌上花开)cdq-分治

P3810 [模板]三维偏序(陌上花开) 思路 :按照 1维排序 二维 分治三维树状数组维护 #include<bits/stdc++.h> using namespace std; #define maxn 234567 int n,k,id,s,tree[maxn*2],tong[maxn]; struct node { int a,b,c,ans,w; node() { ans=0; w=0; } } data[maxn]; bool cp1(node x,node y) { if(x.

【洛谷P3810 三维偏序】

题目描述: 有 n 个元素,第 i 个元素有 ai?,bi,ci? 三个属性,设 f(i,j) 表示满足 aj?≤ai? 且 bj?≤bi? 且 cj?≤ci? 的 j 的数量. 对于 d∈[0,n),求 f(i) = d 的数量. 输入格式: 第一行两个整数n,k分别表示元素数量和最大属性值. 之后 n 行,每行三个整数 ai?.bi?.ci?,分别表示三个属性值. 输出格式: 输出 nn 行,第 d + 1d+1 行表示 f(i) = df(i)=d 的 ii 的数量. 输入样例: 10 3

BZOJ3262/洛谷P3810 陌上花开 CDQ分治 三维偏序 树状数组

原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 落谷P3810 题意 有$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\leq 100000,max\{a_i,b_i,c_i|i

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

题目背景 这是一道模板题 可以使用bitset,CDQ分治,K-DTree等方式解决. 题目描述 有 nn 个元素,第 ii 个元素有 a_iai?.b_ibi?.c_ici? 三个属性,设 f(i)f(i) 表示满足 a_j \leq a_iaj?≤ai? 且 b_j \leq b_ibj?≤bi? 且 c_j \leq c_icj?≤ci? 的 jj 的数量. 对于 d \in [0, n)d∈[0,n),求 f(i) = df(i)=d 的数量 输入输出格式 输入格式: 第一行两个整数 n

P2433 - 【BZOJ 3262三维偏序】陌上花开------三维偏序

P2433 - [BZOJ 3262三维偏序]陌上花开 Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量. 定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,

三维偏序(陌上花开)

三维偏序(陌上花开) 有n个元素,第i个元素有\(a_i\),\(b_i\),\(c_i\)三个属性,设\(f(i)\)表示满足\(a_j\le a_i\)且\(b_j\le b_i\)且\(c_j\le c_i\)的j的数量.对于\(d\in[0, n)\),求\(f(i)=d\)的数量. 偏序关系,意思是并不是任意两个元素之间都有关系.以前在机房里天天听到"偏序""cdq",也知道偏序问题要用cdq分治来做.现在终于会辣! 显然,我们处理的是三元组之间的关系,并

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

【三维偏序】【分块】bzoj3262 陌上花开

裸的三维偏序. 对x坐标排序,y.z坐标分块.复杂度O(n*sqrt(n*log(n))).代码很短. 1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 #include<vector> 5 using namespace std; 6 struct Point{int x,y,z,num;void Read(){scanf("%d%d%d",&x,&

SPOJ LIS2 Another Longest Increasing Subsequence Problem 三维偏序最长链 CDQ分治

Another Longest Increasing Subsequence Problem Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=19929 Description Given a sequence of N pairs of integers, find the length of the longest incre