cdq分治小结

神奇的思想

一般的分治,众所周知的,是通过将大的问题拆小,然后对小问题的答案进行合并得到大问题的答案,但是cdq分治不是。我们知道,分治时,将一个区间从中间斩开,分两半处理,cdq分治在处理完之后,不是合并答案,而是计算左区间对右区间的贡献,这样子可以将维度降低,问题就更好做了。

引子

现在有 n n n 个二元组,每个形如 ( a , b ) (a,b) (a,b),现在问有多少对二元组之间是吊打的关系。Ps: x x x 吊打 y y y 的定义是x的a>y的a , x的b>y的b

大佬瞟一眼就能发现,这就是逆序对问题嘛。

我们对关键字 a a a 进行排序,这样我们就可以忽略 a a a 的影响了,后面用权值树状数组即可求解。

可见,排序是一种强大的降维武器!

进入正题

问题升级!

现在有 n n n 个三元组,每个形如 ( a , b , c ) (a,b,c) (a,b,c),现在问有多少对三元组之间是吊打的关系。Ps: x x x 吊打 y y y 的定义是x的a>y的a , x的b>y的b , x的c>y的c

我们发现,用排序进行降维之后,还剩下两维呀!而排序这个武器是显然只能用一次的,那对于二元组显然也难以求解,于是,我们还需要降维,用谁呢?

神奇的cdq分治登场!

依照上面的思想,我们可以对关键字 b b b 进行分治,每次将序列分成两半,将这两半分别按照关键字 b b b 排序,那么现在我们知道,左边部分的 a a a 都小于右边部分的 a a a(假设我们从小到大排序),那么我们只需要将左边部分中 b b b 也小于右边部分 b b b 的那些加入权值树状数组,然后统计答案即可。

(这么说我自己都觉得抽象。。) 那么来看看代码吧!

void solve(int x,int y)
{
    if(x==y)return;//假如只有一个元素,就没什么好分的了
    int mid=x+y>>1;//分两半
    solve(x,mid);solve(mid+1,y);//将这两半进行分治
    sort(a+x,a+mid+1,cmpy);sort(a+mid+1,a+y+1,cmpy);//将这两半按第二关键字进行排序
    //第一关键字在外面已经排好序了
    int l=x,r=mid+1;//记录当前遍历到的左右部分的下标
    while(r<=y)//假如右边部分还有元素
    {
        while(a[l].y<=a[r].y&&l<=mid)add(a[l].z,a[l].w),l++;
        //将左边部分中第二关键字比他小的塞入权值树状数组
        a[r].ans+=sum(a[r].z);r++;//统计答案
    }
    for(int i=x;i<l;i++)//清空树状数组
    add(a[i].z,-a[i].w);
}

当然,cdq分治的强大之处还不止这样,它不仅和排序一样可以降一维,它还比排序要厉害一些——它可以嵌套!

这样,就可以降更多维了,而时间复杂度仅仅是随着嵌套层数增加若干个log而已!而且,空间上完全无负担。

具体做法可能以后会补上吧qwq,这里先坑一下了

你可能感兴趣的:(算法小结区)