CDQ分治学习笔记

这篇具有很好参考价值的文章主要介绍了CDQ分治学习笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

CDQ分治学习笔记

@

目录
  • CDQ分治学习笔记
    • 前言
    • CDQ分治思想
    • 例题
      • 1、翻转对
        • 分析
        • code
    • P3810 三维偏序(陌上花开)
        • 输入格式
        • 输出格式
        • 样例 #1
          • 样例输入 #1
          • 样例输出 #1
        • 提示
      • 分析
      • code
    • 寒假作业

前言

之前在gdkoi讲解是有人用 \(CDQ\) 分治A了day1 T3。好像分治FFT要用到,而且其他人都学过了,所以蒟蒻再次恶补一手之前的知识点。
\(CDQ\) 显然是一个人的名字,陈丹琪(NOI2008金牌女选手)。

CDQ分治思想

分治就是分治,“分而治之”的思想。
显然CDQ分治不是普通的分治
因为这一类问题又有一个共同特点就是 左区间会对右区间产生贡献
我们一般要先求左区间的答案,然后再求左区间对右区间的贡献,最后求右区间的答案。
前置知识:归并排序求逆序对,不会的可以看一下这个。
树状数组 也要用到一点。

例题

1、翻转对

然后我们看一下这一题:翻转对。
题目传送门
题⽬描述
给定⼀个⻓度为 \(n\) 的数组 \(nums\) ,如果 \(i < j\)\(nums[i] > 2 * nums[j]\), 我们就将 \((i , j)\) , 称作⼀个重要翻转对。你需要返回给定数组中的重要翻转对的数量。
数据规模
\(n ≤ 100000\)

分析

很明显这还只是普通的分治
这题用一个简单的归并排序就可以完成,思路差不多,在归并排序求逆序对的代码稍微改一下,把逆序对变成了翻转对⽽已。

code

很明显这是网上抄的,主要是我的力扣账号忘记密码了

class Solution{
      private int cnt;
      public int reversePairs(int[] nums){
        int len=nums.length;
        sort(nums, Arrays.copyOf(nums, len),0,len-1);
        return cnt;
    }
    private void sort(int[] src,int[] dest,int s,int e){
        if (s>=e){
            return;
        }
        int mid=(s+e)>>1;
        sort(dest,src,s,mid);
        sort(dest,src,mid+1,e);
        merge(src,dest,s,mid,e);
    }
    private void merge(int[] src,int[] dest,int s,int mid,int e) {
        int i=s,j=mid+1,k=s;
        while (i<=mid&&j<=e){
            if((long)src[i]>2*((long)src[j])){
                cnt+=mid-i+1;
                j++;
            } 
            else{
                i++;
            }
        }
        i=s;
        j=mid+1;
        while(i<=mid&&j<=e){
            if (src[i]<=src[j]){
                dest[k++]=src[i++];
            } 
            else{
                dest[k++]=src[j++];
            }
        }
        while(i<=mid){
            dest[k++]=src[i++];
        }
        while(j<=e){
            dest[k++]=src[j++];
        }
    }
}

P3810 三维偏序(陌上花开)

题目传送门
这道题应该是学过 \(CDQ\) 分治的人都做过了(毕竟是版题,还是紫色的)。
有 $ n $ 个元素,第 $ i $ 个元素有 $ a_i,b_i,c_i $ 三个属性,设 $ f(i) $ 表示满足 $ a_j \leq a_i $ 且 $ b_j \leq b_i $ 且 $ c_j \leq c_i $ 且 $ j \ne i $ 的 \(j\) 的数量。

对于 $ d \in [0, n) $,求 $ f(i) = d $ 的数量。

输入格式

第一行两个整数 $ n,k $,表示元素数量和最大属性值。

接下来 $ n $ 行,每行三个整数 $ a_i ,b_i,c_i $,分别表示三个属性值。

输出格式

$ n $ 行,第 $ d + 1 $ 行表示 $ f(i) = d $ 的 $ i $ 的数量。

样例 #1

样例输入 #1
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
样例输出 #1
3
1
3
0
1
0
1
0
0
1

提示

$ 1 \leq n \leq 10^5$,$1 \leq a_i, b_i, c_i \le k \leq 2 \times 10^5 $。

分析

这题是一个三维偏序,我们先看一下把它转换成二维偏序 \(a_j \leq a_i\) \(b_j \leq b_i\) 时怎么做。
我们把 \(a\) 为第一关键字排序,然后用树状数组动态维护维护 \(b_j \leq b_i\) 的个数,时间复杂度:\(O(n\log_2 n)\)

然后考虑本题。
我们还是将 \(a\) 数组为第一关键字,\(b\) 数组为第二关键字,\(c\) 数组为第三关键字排序。
然后用类似于普通分治的方法,将 \([l , r]\) 分成 \([l , mid]\) \([mid + 1 , r]\) 两个区间。两个区间内部按照 \(b\) 数组为第一关键字,\(c\) 数组为第二关键字排序,然后同时遍历两个子区间,利⽤树状数组将左区间对右区间的贡献统计到右区间上。
可能有点难懂,大家可以看一下代码 感性 理解一下,还是比较简单的。

code

#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define LL long long
#define fd(x , y , z) for(int x = y ; x >= z ; x --)
using namespace std;
const int N = 2e5 + 5;
int n , k , tr[N] , ans[N] , m;
struct note {
    int cnt , a , b , c , ans;
}s1[N] , s2[N];
int read () {
    int val = 0 , fu = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') fu = -1;
        ch = getchar (); 
    }
    while (ch >= '0' && ch <= '9') {
        val = val * 10 + (ch - '0');
        ch = getchar ();
    }
    return val * fu;
}
bool comp1 (note x , note y) {
    if (x.a != y.a) return x.a < y.a;
    if (x.b != y.b) return x.b < y.b;
    if (x.c != y.c) return x.c < y.c;
}
int lowbit (int x) { return x & (-x); }
bool comp2 (note x , note y) {
    if (x.b != y.b) return x.b < y.b;
    return x.c < y.c; 
}
void Insert (int val , int sum) {
    int i = val;
    while (i <= k) {
        tr[i] += sum;
        i += lowbit(i);
    }
}
int query (int x) {
    int sum = 0;
    while (x) {
        sum += tr[x];
        x -= lowbit (x);
    }
    return sum;
}
void cdq (int l , int r) {
    if (l == r) return;
    int mid = l + r >> 1;
    cdq (l , mid) , cdq (mid + 1 , r);
    sort (s2 + l , s2 + mid + 1 , comp2);
    sort (s2 + mid + 1 , s2 + r + 1 , comp2);
    int j = l;
    fu (i , mid + 1 , r) {
        while (s2[i].b >= s2[j].b && j <= mid) {
            Insert (s2[j].c , s2[j].cnt);
            j ++;
        }
        s2[i].ans += query (s2[i].c);
    }
    fu (i , l , j - 1)
        Insert (s2[i].c , -s2[i].cnt);
} 
int main () {
    n = read () , k = read ();
    fu (i , 1 , n)
        s1[i].a = read () , s1[i].b = read () , s1[i].c = read ();
    sort (s1 + 1 , s1 + n + 1 , comp1);
    int t = 0;
    s1[n + 1] = {0 , 0 , 0 , 0 , 0};
    fu (i , 1 , n) {
        t ++;
        if (s1[i].a != s1[i + 1].a || s1[i].b != s1[i + 1].b || s1[i].c != s1[i + 1].c) {
            s2[++ m] = s1[i];
            s2[m].cnt = t;
            t = 0;
        }
    }
    cdq (1 , m);
    fu (i , 1 , m)
        ans[s2[i].cnt + s2[i].ans - 1] += s2[i].cnt;
    fu (i , 0 , n - 1) 
        printf ("%d\n" , ans[i]);
    return 0;
}

寒假作业

看这个文章来源地址https://www.toymoban.com/news/detail-427253.html

到了这里,关于CDQ分治学习笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python个人学习笔记目录

    以下目录基于黑马程序员B站视频个人学习笔记 正在更新中……

    2024年01月21日
    浏览(45)
  • 目录IO 2月19日学习笔记

     1. lseek        off_t lseek(int fd, off_t offset, int whence);        功能:             重新设定文件描述符的偏移量        参数:             fd:文件描述符             offset:偏移量             whence:                 SEEK_SET    文件开头                 SEEK_CUR    文件当前

    2024年02月20日
    浏览(35)
  • Linux 学习笔记(2)—— 关于文件和目录

    目录 1、切换目录 2、查看系统信息 3、文本的创建和编辑 3-1)创建文件  3-2)查看文件 3-3)输出重定向和追加重定向等 3-4)使用 vi 编辑器编辑文件 4、文件和文件夹的处理 4-1)对文件的处理  4-2)查看目录信息 4-3)对目录的操作 5、文件查找 6、查找文件中的内容 7、使用

    2024年02月09日
    浏览(68)
  • 【Python_PySide学习笔记(目录)】

    下面是专栏的目录汇总: 序号 标题 1 【Python_PySide2学习笔记(一)】PySide2动态加载UI方式,重写关闭窗体事件 2 【Python_PySide2学习笔记(二)】QTabWidget 添加布局LayoutQTabWidget内控件大小自适应父窗体大小 3 【Python_PySide2学习笔记(三)】QPushButton设置背景图片 4 【Python_PySide

    2024年01月24日
    浏览(67)
  • 安卓学习笔记:安卓11访问/读写 Android/data 目录

    省流提示:采用android studio工具开发,记录一次低级的开发,避免以后忘记或者踩坑。 最近有个业余项目开发到一小半,过程中需要读写 Android/data目录的文件,采用常规的文件操作总是提示权限被拒绝,无奈上网参考了很多资料,终于得到了解决。 无法访问Android/data 的原因

    2024年02月13日
    浏览(45)
  • 【C++】C++学习前言

    C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(objectoriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。

    2024年03月12日
    浏览(56)
  • 课程学习前言

    app 抓包分析可以看到有签名有加固,毕竟需要 APK 去访问服务、获取数据,都需要 APK 有完整的信息,而这些信息、代码经过各种加密,还是放在 APK 里面。说白了,就是门锁紧了,钥匙藏在门口某个地方,也许就是地垫下面 逆向流程 拿到 App 应用的 apk ; 使用工具进行查壳

    2024年02月06日
    浏览(45)
  • Gowin FPGA学习记录——前言

            好久没有写博客了,想想是不是又该写点啥东西了么,准备写点国产FPGA的使用经历吧                  得益于目前国内的政策对国产化芯片扶持,越来越要求核心器件能够自主可控,因此作为核心芯片FPGA,国产FPGA的势头也发展很快。          现在FPGA的这

    2024年02月16日
    浏览(43)
  • 【自制C++深度学习框架】前言

    此GitHub项目是一个初学者的深度学习框架,使用C++编写,旨在为用户提供一种简单、易于理解的深度学习实现方式。以下是本项目的主要特点和功能: 计算图:使用计算图来描述深度学习模型的计算过程,利用计算图将神经网络的计算过程视为一个有向无环图。通过构建计算

    2024年02月07日
    浏览(43)
  • 大数据、人工智能、机器学习、深度学习关系联系前言

    1.大数据和人工智能关系 2.机器学习、深度学习、人工智能关系 3.监督学习、无监督学习、半监督学习、强化学习、迁移学习关系 4.机器学习具体内容 1.数据驱动的人工智能 :人工智能系统需要大量的数据来进行训练和学习。大数据提供了海量的信息,可以用于训练机器学习

    2024年02月12日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包