陌上花开,可缓缓归矣 ——吴越王

每日一学语文 [滑稽]。

BBQ 烤翅,CDQ 分治

读起来很顺口对吧

当然这题 $KDT$ 是可以做的 (或许?),但是不费,所以用 $CDQ$ 算了吧。

很显然这道题是 $CDQ$ 三维偏序的板子题 (luogu 上它本来就是板子题)

$CDQ$ 分治的三维偏序怎么做?

对于其中的第一维,$CDQ$之前直接 $sort$ 排好序,那么这就可以保证对于一个 $i<j$ ,位置 $j$ 的元素一定是对位置 $i$ 的元素做不出贡献的,因为 $x_i <x_j$ 。

然后第二维,进入 $CDQ$ ,很显然当前的区间 $l – r$ 是会分成两个子区间分别做 $CDQ$ 的,那么当两个子区间合并的时候,左子区间是可能会对右子区间做出贡献的,但是右子区间对左子区间做不出任何贡献,原因是我们在之前已经按 $x$ 排好了序,那么显然左子区间的元素的 $x$ 始终小于右子区间的元素的 $x$。

外面排好了第一维,那么我们就在 $CDQ$ 中排第二维,由于我们是分成了两个子区间递归处理,往上面合并的时候,正好可以归并排序。第三位只需要在树状数组中记录一下,然后统计答案的时候调用树状数组的查询,看看比当前元素小的有多少个即可。

void CDQ(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);CDQ(mid+1,r);/*分成两个子区间*/
    int i=l,j=mid+1,cnt=l;
    while(i<=mid&&j<=r){/*归并+统计答案*/
        if(v[i].b<=v[j].b)add(v[i].c,size[v[i].id]),hep[cnt++]=v[i++];
        /*左子区间的当前元素可能会有贡献,记录一下*/
        else ans[v[j].id]+=sum(v[j].c),hep[cnt++]=v[j++];
        /*接下来的左子区间的 b 是比当前的 j.b 要大的了,没有贡献了*/
        /*因为在子区间中使用了归并,所以两个子区间中 b 肯定是升序的*/
    }
    while(j<=r)ans[v[j].id]+=sum(v[j].c),hep[cnt++]=v[j++];
    /*将剩下的归并排序完*/
    for(int h=l;h<i;++h)add(v[h].c,-size[v[h].id]);
    /*清除树状数组留下的痕迹*/
    while(i<=mid)hep[cnt++]=v[i++];
    for(int i=l;i<=r;++i)v[i]=hep[i];
    /*更新原数组*/
}

$QvQ$ 就这样了,只是这题需要离散化一下,$Code$ 中的 $size$ 就是元素出现的个数。

Code:

#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>

#define ll long long

const int N=1e5+2;
const int K=2e5+2;

int n,k;
struct Node{int a,b,c,id;}v[N],hep[N];
int tre[K],ans[N],num[N],size[N];

inline void add(int x,int v){for(;x<=k;x+=(x&-x))tre[x]+=v;};
inline ll sum(int x){ll res=0;for(;x;x-=(x&-x))res+=tre[x];return res;};

template<typename _Tp> inline void IN(_Tp&x){
    char ch;bool flag=0;x=0;
    while(ch=getchar(),!isdigit(ch))if(ch=='-')flag=1;
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    if(flag)x=-x;
}

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

void CDQ(int l,int r){
    if(l==r)return;
    int mid=(l+r)>>1;
    CDQ(l,mid);CDQ(mid+1,r);
    int i=l,j=mid+1,cnt=l;
    while(i<=mid&&j<=r){
        if(v[i].b<=v[j].b)add(v[i].c,size[v[i].id]),hep[cnt++]=v[i++];
        else ans[v[j].id]+=sum(v[j].c),hep[cnt++]=v[j++];
    }
    while(j<=r)ans[v[j].id]+=sum(v[j].c),hep[cnt++]=v[j++];
    for(int h=l;h<i;++h)add(v[h].c,-size[v[h].id]);
    while(i<=mid)hep[cnt++]=v[i++];
    for(int i=l;i<=r;++i)v[i]=hep[i];
}

int main(){
    IN(n),IN(k);
    for(int i=1;i<=n;++i)
       scanf("%d%d%d",&v[i].a,&v[i].b,&v[i].c);
    std::sort(v+1,v+n+1,cmp);
    int tot=0;
    for(int i=1;i<=n;++i){
        if(v[i].a!=v[i-1].a||v[i].b!=v[i-1].b||v[i].c!=v[i-1].c)hep[++tot]=v[i];
        ++size[tot];
    }
    for(int i=1;i<=tot;++i)v[i]=hep[i],v[i].id=i;
    CDQ(1,tot);
    for(int i=1;i<=tot;++i)
       num[ans[v[i].id]+size[v[i].id]-1]+=size[v[i].id];
    for(int i=0;i<n;++i)
       printf("%d\n",num[i]);
    return 0;
}
分类: 文章

Qiuly

QAQ

3 条评论

Qiuly · 2019年2月22日 10:16 下午

QvQ 但是我是 CDQ 初学者,代码丑陋……..CDQ 跟树套树貌似没关系吧…… 您佬还会六维的 CDQ%%%Tql

    quhengyi11 · 2019年2月23日 12:30 下午

    只是这题能用树套树了啦 qwq 所以我就拿这题练习一下(尽管好像需要卡常

quhengyi11 · 2019年2月22日 10:10 下午

我 今晚 用 线段树套 splay(练习码力) 调这题心态崩溃了 QwQ 然后就看到了这篇教程 QAQ (日常吐槽.jpg

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注