[浅谈] 分治
\(\color{purple}\text{Ⅰ.CDQ分治}\)
\(\color{orange}\text{概念}\)
CDQ分治,又称基于时间的分治算法,常用于解决多维偏序问题。该算法可以通过增加 \(\log n\) 的代价将偏序问题降掉一维,从而转化成更易解决的多维偏序问题。事实上,CDQ分治能解决的题目很多都可以用支持动态查询的高级数据结构完成,但是CDQ分治的思维难度和代码实现难度较于高级数据结构减小了很多,并且空间更小。
——「子相詹」
\(\color{orange}\text{CF12D Ball}\)
求出被同学文化课,竞赛,颜值全部碾压的可怜孩子的数量。
考虑先按文化课排序,然后将排序后的数组分成两半,左右的子区间递归处理,我们只需要考虑左右区间之间的贡献。
那么对于左右区间分别按照竞赛从强到弱排序,然后双指针做就好了,看看对于每个人,文化课,竞赛强于他的人中,颜值最高的同学是否能碾压他。
考虑等于的情况,竞赛,颜值的等于十分好处理。唯独这个文化课,我们只能保证右半区间的人的文化课大于等于左半区间的人。所以我们得特殊考虑等于的情况怎么解决:记录两个颜值最大值,一个是文化课一定大于左区间的人中的颜值最大值,一个是文化课大于等于左区间的人中的颜值最大值。然后对于文化课等于左右区间边界的人,用前者更新答案,否则用后者更新答案。
这还是挺妙的。当然也不是特别妙。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+110;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct Element{
int a,b,c,suicide;
}e[N];
bool cmp1(Element A,Element B){return A.a<B.a;}
bool cmp2(Element A,Element B){return A.b>B.b;}
int n,k,ans;
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1,bz=0,mx1=0,mx2=0;
for(int i=l;i<=mid;i++)bz=max(bz,e[i].a);
cdq(l,mid);cdq(mid+1,r);
sort(e+l,e+mid+1,cmp2);sort(e+mid+1,e+r+1,cmp2);
for(int i=l,j=mid+1;i<=mid;i++){
while(j<=r && e[j].b>e[i].b){
mx1=max(mx1,e[j].c);
if(e[j].a!=bz)mx2=max(mx2,e[j].c);
j++;
}
if(e[i].a==bz)if(mx2>e[i].c)e[i].suicide=1;
else if(mx1>e[i].c)e[i].suicide=1;
}
return;
}
int main(){
n=read();
for(int i=1;i<=n;i++)e[i].a=read();
for(int i=1;i<=n;i++)e[i].b=read();
for(int i=1;i<=n;i++)e[i].c=read();
for(int i=1;i<=n;i++)e[i].suicide=0;
sort(e+1,e+n+1,cmp1);cdq(1,n);
for(int i=1;i<=n;i++)if(e[i].suicide)ans++;
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号