陌上花开,可缓缓归矣 ——吴越王
每日一学语文 [滑稽]。
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;
}
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