首页 > 其他分享 >CDQ分治

CDQ分治

时间:2024-10-06 16:12:08浏览次数:9  
标签:输出 数列 int 分治 mid 整数 CDQ 逆序

前置知识


 

归并排序(一维偏序)


 

推荐写法:https://www.cnblogs.com/didiao233/p/17986130 第1题     归并排序 查看测评数据信息

给定你一个长度为 n 的整数数列。

请你使用归并排序对这个数列按照从小到大进行排序。

并将排好序的数列按顺序输出。

输入格式

 

输入共两行,第一行包含整数 n。

第二行包含 n 个整数(所有整数均在 1-1e9 范围内),表示整个数列。

 

输出格式

 

输出共一行,包含 n 个整数,表示排好序的数列。

 

输入/输出例子1

输入:

5

3 1 2 4 5

 

输出:

1 2 3 4 5

 

样例解释

 

一维偏序:归并(T1)

看一下讲义就行,这是根据讲义写出的代码,可能有点抽象。

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;

int n, a[N], tmp[N], tmp2[N];
void merge(int L, int R)
{
	if (L>=R) return ;
	
	int mid=L+R>>1, m=0, m2=0, idx=0, idx2=0;
	merge(L, mid);
	merge(mid+1, R);
	
	for (int i=L; i<=mid; i++) tmp[++m]=a[i];
	for (int i=mid+1; i<=R; i++) tmp2[++m2]=a[i];
	
	tmp[++m]=tmp2[++m2]=2e9, idx=idx2=1;
	for (int i=L; i<=R; i++) 
	{
		if (tmp[idx]<tmp2[idx2]) a[i]=tmp[idx++];
		else a[i]=tmp2[idx2++];
	}
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &a[i]);
	
	merge(1, n);
	
	for (int i=1; i<=n; i++) printf("%d ", a[i]);
	
	return 0;
}

  

 

逆序对(二维偏序)


 

推荐写法:https://www.cnblogs.com/didiao233/p/17986130 第2题     逆序对 查看测评数据信息

给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式

 

第一行包含整数 n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

1≤n≤100000,

数列中的元素的取值范围 [1,1e9]。

 

输出格式

 

输出一个整数,表示逆序对的个数。

 

输入/输出例子1

输入:

6

2 3 4 5 6 1

 

输出:

5

 

样例解释

 

二维偏序:逆序对(T2)
i<j 且 a[i]>a[j]

 

分3类,左,右,跨越左右

左,右很好求,分成子问题去求就好了,关键在于如何求跨越左右的影响?

 

我们就看左边对右边的影响就行了,因为此时默认满足了i<j的条件,只用考虑满足  a[i]>a[j] 即可。

 

此时,经过不断归并,左边的数经过排序会单调递增,右边的数也会单调递增。

 

所以在双指针的时候顺带判断一下即可。
只要当前的数a[i]>a[j],那么i后面的数也满足这个条件,所以答案+=mid-i+1即可。

 

 

同上,看讲义写出来的:

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;

int n, a[N], tmp[N], tmp2[N];
long long merge(int L, int R)
{
	if (L>=R) return 0;
	
	int mid=L+R>>1, m=0, m2=0, idx=0, idx2=0;
	long long res=merge(L, mid)+merge(mid+1, R);
	
	for (int i=L; i<=mid; i++) tmp[++m]=a[i];
	for (int i=mid+1; i<=R; i++) tmp2[++m2]=a[i];
	
	tmp[++m]=tmp2[++m2]=2e9, idx=idx2=1;
	for (int i=L; i<=R; i++) 
	{
		if (tmp[idx]<=tmp2[idx2]) a[i]=tmp[idx++];
		else a[i]=tmp2[idx2++], res+=mid-(L+idx-1)+1;
	}
	
	return res;
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%d", &a[i]);
	
	printf("%lld", merge(1, n));
	
//	for (int i=1; i<=n; i++) printf("%d ", a[i]);
	
	return 0;
}

  

 

 

距离(二维偏序进阶)


第3题     距离 查看测评数据信息

高一小z刚学习物理的匀速直线运动,所以他来到机房做一道关于匀速直线运动的信息学题目,在一条无限长的直线上有n个点,每个点有如下属性:

(1)第i个点在数轴上的位置是d(i)

(2)每个点有一个初始的速度v(i)

(3)每个点有一个运动的方向,当v(i)为正数时,代表点i从初始位置向右进行匀速直线运动,反之则向左进行匀速直线运动。如t秒后,i点所在的位置是d(i)+v(i)*t

令f(i,j)表示i点和j点在任意时刻可能存在的距离的最小值。

现在请帮小z求出所有的f(i,j)的总和(其中1<=i<j<=n)

输入格式

 

第一行一个整数n,表示数轴上有n个点(2<=n<=2e5)

第二行n个整数,x(1),x(2),x(3),...x(n-1),x(n)

第三行n个整数,v(1),v(2),v(3),...v(n-1),v(n)

1<=x(i)<=1e8

-1e8<=v(i)<=1e8

 

输出格式

 

一个整数,表示最大值

 

输入/输出例子1

输入:

3

1 3 2

-100 2 3

 

输出:

3

 

样例解释

 

可以先考虑分类。也就是追得上和追不上的问题。

d[i]<d[j] && v[i]>v[j],追得上。答案:0

d[i]<d[j] && v[i]<=v[j] 追不上,答案: d[j]-d[i]

d[i]>d[j] && v[i]>=v[j],这里是和上面的情况一样的,相当于是d,v都取反,计算答案也取反了,不用再单独去算。

 

也就转换成了求二维偏序。

 

按v排序,v[i]<=v[j] 的条件就满足了。

对于d[i]<d[j]的条件,分类就行了
1.左边
2.右边
3.跨越左右

 

考虑第3类,同T2。

因为归并的时候已经让左边,右边变成单调递增的了,所以在求这个条件的时候用双指针即可。

 

如果d[i]<d[j],那么对于j后面的数,此时的i也满足的!
所以答案+=R-j+1(用前缀和优化,也就是d[j]-d[i]  +  d[j+1]-d[i]这样)

如果按照此写法,排序的时候记得按w为第二关键字排序,否则会进不去计算res的条件

if (a[i].x<=a[j].x) res+=(s[R]-s[j-1])-a[i].x*(R-j+1), tmp[++m]=a[i++].x;

 

 

或者说换一种思路:找第一个d[i]>d[j],那么对于i前面的数肯定都<d[j],所以答案+=i-L+1(用前缀和优化,同上。)

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;

struct node
{
	ll x;
	int v;
}a[N];
int n, tmp[N], m=0;
ll s[N];
bool cmp(node a, node b)
{
	if (a.v==b.v) return a.x<b.x;
	return a.v<b.v;
}
ll cdq(int L, int R)
{
	if (L>=R) return 0;
	
	int mid=L+R>>1, i=0, j=0;
	ll res=cdq(L, mid)+cdq(mid+1, R);
	
	for (int i=L; i<=R; i++) s[i]=s[i-1]+a[i].x;
	
	m=0, i=L, j=mid+1;
	while (i<=mid && j<=R)
	{
		if (a[i].x<=a[j].x) res+=(s[R]-s[j-1])-a[i].x*(R-j+1), tmp[++m]=a[i++].x;
		else tmp[++m]=a[j++].x;
	}
	while (i<=mid) tmp[++m]=a[i++].x;
	while (j<=R) tmp[++m]=a[j++].x;
	
	for (int i=L, j=1; i<=R, j<=m; i++, j++) a[i].x=tmp[j];
	
	return res;
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) scanf("%lld", &a[i].x);
	for (int i=1; i<=n; i++) scanf("%d", &a[i].v);
	
	sort(a+1, a+1+n, cmp);
	
	printf("%lld", cdq(1, n));
	return 0;
}
/*
2
2 1
2 2

*/

  

 

标签:输出,数列,int,分治,mid,整数,CDQ,逆序
From: https://www.cnblogs.com/didiao233/p/18449152

相关文章

  • 洛谷题单指南-分治与倍增-P6648 [CCC2019] Triangle: The Data Structure
    原题链接:https://www.luogu.com.cn/problem/P6648题意解读:在一个n行的数字三角形中,求所有边长为k的正三角形最大值之和。解题思路:1、枚举法枚举每一个边长为k的三角形,在其中求max,然后累加,n最多3000,时间复杂度是n^4,显然超时。2、倍增和ST思想此题非常类似于RMQ问题,也就是求区......
  • [2023四校联考3]sakuya 题解(根号分治)
    题目链接。题目分析第一个操作类似哈希冲突那一道题,可以运用类似的思路开一个二维表,很容易想到两种做法:开一个二维表,表上的第\(i\)行,第\(j\)列表示序列下标在模\(i\)意义下等于\(j\)的加法标记。对于修改操作,直接暴力修改对应的那一行的值即可,查询时用线段树查询那个......
  • 99th 2024/9/4 CDQ分治小结
    概括轻新小思路用于处理点对关系题在能将点对分段处理(如下)的题目中有奇效试想,现在要处理部分点对,其范围为\(l\in[L,R],r\in[L,R]\)其位置不确定,但是我们可以考虑将其分为三个板块分别作出的总贡献即分为\(l\in[L,mid],r\in[mid+1,R]\)\(l\in[L,mid],r\in[L,mid]\)\(l......
  • CDQ分治学习笔记
    CDQ分治学习笔记k维偏序问题求满足条件的二元组个数题意描述每个元素有\(k\)个值,要求满足(以\(k=2\)为例)\(a_j\lea_i,b_j\leb_i\)的点对个数。分析这实际上就是我们熟悉的逆序对问题,回忆一下我们是怎么处理的,首先来说,当\(a,b\)其中一个的含义是下标的时候可以直......
  • 树分治
    点分治点分治适合处理大规模的树上路径信息问题。本质思想是选择一点作为分治中心,将原问题划分为几个相同的子树上的问题,进行递归解决。常见的用于统计树上有关路径的信息。假设当前选定根节点为\(rt\),则符合条件的路径必然是以下两种之一:经过\(rt\)或一段在\(rt\)上;在......
  • 揭秘合并排序:分治排序初学者指南
    归并排序由约翰·冯·诺依曼于1945年提出,主要是为了提高大型数据集的排序效率。冯·诺依曼的算法旨在使用分而治之的方法提供一致且可预测的排序过程。这种策略允许归并排序有效地处理小型和大型数据集,保证在所有情况下都能实现稳定的排序,时间复杂度为o(nlogn)。合并排序采用......
  • 洛谷题单指南-分治与倍增-P4155 [SCOI2015] 国旗计划
    原题链接:https://www.luogu.com.cn/problem/P4155题意解读:在m个点的环上,有n个区间,且各个区间之间没有包含关系,计算从每个区间开始,最少要多少个区间能覆盖环上所有m个点。解题思路:本质上是一个区间覆盖问题!1、破环成链由于题目中是一个环,对于环的问题,在区间DP中介绍过,经典处理......
  • 深入分治法:如何用简单步骤解决复杂问题?
    分治法(DivideandConquer)概述分治法是一种经典的算法设计思想,它将一个复杂问题分成多个规模较小的子问题,分别解决这些子问题,然后将子问题的解组合成原问题的解。分治法的思想非常通用,适用于许多经典算法和问题,如归并排序、快速排序、二分搜索、矩阵乘法等。分治法的基本步......
  • 洛谷题单指南-分治与倍增-P3517 [POI2011] WYK-Plot
    原题链接:https://www.luogu.com.cn/problem/P3517题意解读:有n个连续的点p1,p2,...,pn,将这n个点分成不超过m堆,每堆点连续,每一堆都缩成一个点qi,要使得原来的点p1~ps距离qi的最大值最小(最相似),求这个相似度,并计算一共分成几堆,以及每堆缩到的点qi的坐标。解题思路:要使得若干点缩到一......
  • 归并分治
    归并排序912.排序数组#include<iostream>#include<vector>usingnamespacestd;classSolution{public://分治-治voidmerge(vector<int>&arr,intleft,intmid,intright,vector<int>&temp){//[left,mid]和[mi......