三維偏序(陌上花開)---洛谷P3810&&BZOJ3262(cdq分治--歸併排序+樹狀數組)

洛谷題目鏈接https://www.luogu.org/problem/P3810

BZOJ題目鏈接https://www.lydsy.com/JudgeOnline/problem.php?id=3262

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行,每行三個整數si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的屬性

Output

包含N行,分別表示評級爲0...N-1的每級花的數量。

Sample Input

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

Sample Output

3
1
3
0
1
0
1
0
0
1


。。。蜜汁BUG,重構之後才過了QAQ

三維偏序比二維偏序多了一維,但我們還是可以按照二維偏序的思路來想。先固定住第一維,然後對於第二維用歸併排序,第三維用樹狀數組。

我們在歸併的時候考慮 [l,mid] 對 [mid+1,r] 的貢獻。因爲我們已經按屬性 a 排過序了,所以在排序屬性 b 的時候,無論屬性 a 怎麼被打亂,[mid+1,r] 所有元素的屬性 a 一定不小於 [l,mid] 中所有元素的屬性 a,那麼我們就可以把[l,mid]內的屬性z一個個放到樹狀數組裏面統計了,和二維偏序是一樣的做法。接下來還有一個比較重要的就是重複元素--由於這並不是真真的座標,所以他們會重和,所以我們需要對重複的元素進行剔除和累加。

以下是AC代碼:

#include <bits/stdc++.h>
using namespace std;

const int mac=1e5+10;
const int maxn=2e5+10;

int n,m;
int ans[mac],c[maxn];

struct node
{
    int x,y,z,ans,f;
}a[mac],b[mac];

bool cmp(node a,node b)
{
    if (a.x!=b.x) return a.x<b.x;
    if (a.y!=b.y) return a.y<b.y;
    return a.z<b.z;
}

int lowbit(int x)
{
    return x&-x;
}

void update(int pos,int val)
{
    while (pos<=m){
        c[pos]+=val;
        pos+=lowbit(pos);
    }
}

int query(int pos)
{
    int sum=0;
    while (pos>0){
        sum+=c[pos];
        pos-=lowbit(pos);
    }
    return sum;
}

void cdq(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    int ll=l,rr=mid+1,cnt=l-1;
    while (ll<=mid && rr<=r){
        if (a[ll].y<=a[rr].y){//這個時候後半段對前半段是不會產生貢獻的,所以不用統計前半段的答案
            update(a[ll].z,a[ll].f);
            b[++cnt]=a[ll++];
        }
        else {
            a[rr].ans+=query(a[rr].z);
            b[++cnt]=a[rr++];
        }
    }
    while (ll<=mid){
        update(a[ll].z,a[ll].f);//實際上這裏的這個是沒有用的,只不過是爲了方便之後的清空
        b[++cnt]=a[ll++];
    }
    while (rr<=r){
        a[rr].ans+=query(a[rr].z);
        b[++cnt]=a[rr++];
    }
    for (int i=l; i<=mid; i++) update(a[i].z,-a[i].f);
    cnt=0;
    for (int i=l; i<=r; i++) a[i]=b[i];
}

int main()
{
    scanf ("%d%d",&n,&m);
    for (int i=1; i<=n; i++){
        scanf ("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        a[i].f=1;
    }
    sort(a+1,a+1+n,cmp);
    int cnt=1;
    for (int i=2; i<=n; i++){
        if (a[i].x==a[cnt].x && a[i].y==a[cnt].y && a[i].z==a[cnt].z)
            a[cnt].f++;//f保留的是重複元素的個數
        else a[++cnt]=a[i];
    }
    cdq(1,cnt);
    for (int i=1; i<=cnt; i++){
        ans[a[i].ans+a[i].f-1]+=a[i].f;//由於=是成立的,所以我們要加上相等的個數
    }
    for (int i=0; i<n; i++)
        printf ("%d\n",ans[i]);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章