nlogn求逆序对&&陌上花开
前置:
nlogn逆序对:
前一个小时我还真的不会这个Orz
这里运用归并排序的思想。
对于一个序列,我们把它先分开,再合并成一个有序序列。
引自https://blog.csdn.net/qq_30189255/article/details/50937307
假设f(i,j)为i到j号元素中的逆序对个数,取一个分割点k,假设s(i,j,k)表示以k为分割点,第一个元素在i到k中,第二个元素在k+1到j中形成的逆序对数。那么我们就得到一个递归式:f(i,j)=f(i,k)+f(k+1,j)+s(i,j,k)。很自然的与归并排序联系到了一起,对于更小规模的f可以递归求解,如果对于a数组的i到k以及k+1到j两个部分元素均为有序的情况,那么对于当a[j]<a[i]的情况,必然有a[j]<a[i..k],即a[j]和i到k号元素都形成逆序对,此时只要将s(i,j,k)加上k-i+1就可以了(i到k之间的元素个数),这个过程很像归并排序的Merge的过程——比较当前i和j的状态并放入较小的。于是我们就得到了算法,和归并排序一起操作:外围设置一个计数器count,每次归并过程分为分割与合并两个部分,分割照常处理,在合并部分中的if (a[i]>a[j]) b[++l]=a[++j];中加入一个计数过程,即cnt+=k-i+1。这样当排序完成后,统计也就完成了。
代码如下
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,a[50005],b[50005];
int merge(int l,int r) {
int ans=0;
if(l>=r) return 0;
int mid=l+r>>1;
ans+=merge(l,mid),ans+=merge(mid+1,r);
int i=l,j=mid+1,k=0,cnt=0;
while(i<=mid&&j<=r) {
if(a[i]<=a[j]) b[k++]=a[i++];
else cnt+=mid-i+1,b[k++]=a[j++];
}
while(j<=r) b[k++]=a[j++];
while(i<=mid) b[k++]=a[i++];
for(k=0;k<r-l+1;k++) {
a[l+k]=b[k];
}
return cnt+ans;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
cout<<merge(1,n);
}
这种问题又叫二维偏序,因为有两个维度:位置,数值
那么三维偏序呢?
IOI2019金牌选手GhostCai发明的czq分治
dalao陈丹琦发明的算法叫cdq分治
例题:陌上花开https://www.luogu.org/problemnew/show/P3810
cdq+树状数组 引自 https://blog.csdn.net/reverie_mjp/article/details/52462651
【对于本题来说,每个操作包含三维,首先按第一维关键字排序,并去重,数组中记录相同的花有多少朵。然后CDQ分治处理,处理时,将[l,mid]区间和[mid+1,r]区间分别按第二维关键字排序,并用树状数组以第三维为下标,维护每一朵花的出现次数。每一次处理[l,mid]对[mid+1,r]的影响时,只需考虑第二维的影响即可(因为[l,mid]区间的x一定小于[mid+1,r]区间的x,而第三维用树状数组维护也不需要考虑),当第二维符合要求时,将它的影响加入树状数组中。每查找完[mid+1,r]区间的一个操作,就更新答案】
#include <cstdio>
#include <algorithm>
using namespace std;
int t[500005];
int n,m;
const int N=500005;
struct Node {
int x,y,z,cnt,ans;
bool operator == (const Node &rhs)const {
return x==rhs.x&&y==rhs.y&&z==rhs.z;
}
} a[N],stk[N];
bool cmp(Node x,Node y) {
return x.x==y.x?(x.y==y.y?x.z<y.z:x.y<y.y):x.x<y.x;
}
void add(int x,int y) {
for(int i=x; i<=m; i+=i&-i)
t[i]+=y;
}
int ask(int x) {
int res=0;
for(int i=x; i; i-=i&-i)
res+=t[i];
return res;
}
void merge(int l,int r) {
if (l==r)
return;
int mid=l+r>>1;
merge(l,mid);
merge(mid+1,r);
int i=l,j=mid+1,k=0;
while(i<=mid&&j<=r) {
while(i<=mid&&a[i].y<=a[j].y) add(a[i].z,a[i].cnt),stk[++k]=a[i++];
while(j<=r&&a[j].y<a[i].y) a[j].ans+=ask(a[j].z),stk[++k]=a[j++];
}
while(i<=mid) add(a[i].z,a[i].cnt),stk[++k]=a[i++];
while(j<=r) a[j].ans+=ask(a[j].z),stk[++k]=a[j++];
for(int i=l; i<=mid; i++) add(a[i].z,-a[i].cnt);
for(int i=l; i<=r; i++) a[i]=stk[i-l+1];
}
int d[N];
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].cnt=1;
}
sort(a+1,a+1+n,cmp);
int cnt=0;
for(int i=1; i<=n; i++) {
if(a[i]==a[i-1]&&i!=1) a[cnt].cnt++;
else cnt++,a[cnt]=a[i];
}
swap(n,cnt);
merge(1,n);
for(int i=1; i<=n; i++) d[a[i].ans+a[i].cnt]+=a[i].cnt;
for(int i=1; i<=cnt; i++) printf("%d\n",d[i]);
}
nlogn求逆序对&&陌上花开的更多相关文章
- POJ2299Ultra-QuickSort(归并排序 + 树状数组求逆序对)
树状数组求逆序对 转载http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 转载: 树状数组,具体的说是 离散化+树 ...
- 归并排序+归并排序求逆序对(例题P1908)
归并排序(merge sort) 顾名思义,这是一种排序算法,时间复杂度为O(nlogn),时间复杂度上和快排一样 归并排序是分治思想的应用,我们先将n个数不断地二分,最后得到n个长度为1的区间,显然 ...
- HDU 3743 Frosh Week(归并排序求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3743 题目意思就是给你一个长为n的序列,让你求逆序对.我用的是归并排序来求的.归并排序有一个合并的过程 ...
- AC日记——codevs 1688 求逆序对
1688 求逆序对 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 给定一个序列a1,a2,…, ...
- codevs1688 求逆序对
题目描述 Description 给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目 数据范围:N<=105.Ai<=105. ...
- HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少 ...
- SGU 180 Inversions(离散化 + 线段树求逆序对)
题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=180 解题报告:一个裸的求逆序对的题,离散化+线段树,也可以用离散化+树状数组.因为 ...
- 树状数组求逆序对:POJ 2299、3067
前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换 ...
- wikioi 1688 求逆序对
/*=========================================================== wikioi 1688 求逆序对 时间限制: 1 s 空间限制: 12800 ...
随机推荐
- Android Internet - WebView 的使用
WebView是Android 提供的操作网页的一个组件. 用于浏览网页及其它Internet资源. 这里总结了一些WebView 的经常使用接口.和2个小演示样例程序用于自己开发时直接使用.就不用再 ...
- hdu1716(库函数next_permutation)
题目意思: 现有四张卡片,用这四张卡片能排列出非常多不同的4位数,要求按从小到大的顺序输出这些4位数. 注意首位没有前导0 pid=1716">http://acm.hdu.edu.c ...
- Linux 服务具体解释
acpid ACPI(全 称 Advanced Configuration and Power Interface)服务是电源管理接口. 建议全部的笔记本用户开启它. 一些server可能不须要 ac ...
- Webx框架:Spring Schema 和 Spring Ext
webx诞生的原因是当时市面上没有好用的web框架.如今的Web框架有非常多.然后它们背后的思想都是相似的,并且越来越趋同. Spring Schema 在传统的spring中,配置bean时须要手动 ...
- HTML的表单form以及form内部标签
<html> <head> <title> form表单的使用 </title> <!-- 标签名称:form 表单标签 属性:action:提交 ...
- 利用SharedPreferences完成记住账号密码的功能
利用SharedPreferences完成记住账号密码的功能 效果图: 记住密码后,再次登录就会出现账号密码,否则没有. 分析: SharedPreferences可将数据存储到本地的配置文件中 Sh ...
- Dragon Ball--hdoj
Dragon Ball Problem Description Five hundred years later, the number of dragon balls will increase u ...
- Elo rating system(Elo 打分体系)
A.B 两个待比较.评价的对象,分别打分为 RA,RB,则各自获胜的期望值为: ⎧⎩⎨⎪⎪⎪⎪⎪⎪EA=11+10(RB−RA)/400.EB=11+10(RA−RB)/400. 不妨令 QA=10R ...
- OpenCASCADE 包说明
转载地址:http://www.cppblog.com/eryar/archive/2012/06/30/180916.html 一.简介 Introduction to Package gp gp是 ...
- HDU 3018 一笔画问题
题意:给你一个图 判断最少用几笔把这个图画出来(画过的边不能重新画) 思路: 并查集+欧拉回路 仔细想一想. 在一个强连通分量中 所有度为奇数的点之和÷2就是要画的笔画数 Now question : ...