並不對勁的cdq分治解三維偏序


為了反駁隔壁很對勁的太刀流,並不對勁的片手流決定與之針鋒相對,先一步發表cdq分治解三維偏序。

很對勁的太刀流在這里-> 

參照一、二維偏序的方法,會發現一位偏序就是直接排序,可以看成通過排序使第一維無效。二維偏序是排序+樹狀數組,就是先通過排序消除了第一維的影響,再通過樹狀數組進行統計。那么以此類推,三位偏序應該就是樹套樹狀數組…啊不對,是先通過排序消除第一維的影響,再通過【某種方法】消除第二維的影響,再用樹狀數組統計。

傳說中的【某種方法】就是cdq分治,它是一種通過計算前一半對后一半的影響的降維手段。

具體來說,假設三維分別是x,y,z,先按x排序。分治時每次將前半邊、后半邊分別按y排序。雖然現在x的順序被打亂了,但是前半邊還是都小於后半邊的,所以要是只計算前半邊對后半邊的偏序關系,是不會受到x的影響的。維護后一半的指針i,前一半的指針j,每次將i后移一位時,若y[j]<=y[i]則不斷后移j,並不斷將z[j]加入樹狀數組。然后再查詢樹狀數組中有多少數小於等於z[i]。 最后要清空樹狀數組。

還有“偏序問題中出現了完全相同的要把它們合並”、“清空樹狀數組時要減回去否則時間超限”、“前大括號必須放在下面“這些細節在此就不提了。

然后就會發現,一維偏序也可以cdq(雖然大部分人叫它歸並排序)、樹狀數組做,二維偏序也可以cdq做。也就是說,這些降維手段用在第幾維都可以。那么會不會有n維偏序,cdq套cdq什么的呢?據說那樣復雜度就會在n logkn,還不如n^2暴力枚舉。

 

其實cdq應該不會只局限於偏序問題,也許會有整體二分之類的的離線方法是參照cdq的算出前一半對后一半的影響這種思想呢?

不過強制在線就GG了。

垃圾太刀!!!

 

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define maxn 100010
#define maxk 200010
#define ll long long 
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(isdigit(ch)==0 && ch!='-')ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(int x)
{
    int f=0;char ch[20];
    if(!x){puts("0");return;}
    if(x<0){putchar('-');x=-x;}
    while(x)ch[++f]=x%10+'0',x/=10;
    while(f)putchar(ch[f--]);
    putchar('\n');
}
typedef struct node
{
    int x,y,z,ans,w;    
}stnd;
stnd a[maxn],b[maxn];
int n,cnt[maxk];
int k,n_;
bool cmpx(stnd u,stnd v)
{
    if(u.x==v.x)
    {
        if(u.y==v.y)
            return u.z<v.z;
        return u.y<v.y;
    }
    return u.x<v.x;
}
bool cmpy(stnd u,stnd v)
{
    if(u.y==v.y)
        return u.z<v.z;
    return u.y<v.y;
}
struct treearray
{
    int tre[maxk],kk;
    int lwbt(int x){return x&(-x);}
    int ask(int i){int ans=0; for(;i;i-=lwbt(i))ans+=tre[i];return ans;}
    void add(int i,int k){for(;i<=kk;i+=lwbt(i))tre[i]+=k;}
}t;
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++)
    {
        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 main()
{
    n_=read(),k=read();t.kk=k;
    for(int i=1;i<=n_;i++)
        b[i].x=read(),b[i].y=read(),b[i].z=read();
    sort(b+1,b+n_+1,cmpx);
    int c=0;
    for(int i=1;i<=n_;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++)
        cnt[a[i].ans+a[i].w-1]+=a[i].w;
    for(int i=0;i<n_;i++)
        write(cnt[i]);
    return 0;
}
並不對勁的cdq

 

宣傳一波電教(現在是電教G了(現在是電教X了(現在是電教XX了))),歡迎加入。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM