树状数组 --- (离散化+树状数组、求逆序对)
#
G.Inversions #
Input#
Output#
Sample Input#
Sample test(s)
Input
2 3 1 5 4
Output
首先定义一个结构体:
1 2 3 4 5 6 | struct Node { long long num; int index; }; Node node[MAX]; |
和一个数组a[MAX],然后输入五个数:8 1 6 7 4
1 2 3 4 5 | for (i=1;i<=n;++i) { scanf ( "%lld" ,&node[i].num); node[i].index=i; } |
node[i].num存储了数组的值,而node[i].index存储的是他的下标,也就是序号。
按照num来进行排序:
1 2 3 4 5 | bool cmp(Node a,Node b) { return a.num<b.num; } stable_sort(node+1,node+n+1,cmp); /**< 这是一个重点,后面会单独讲一下。 */ |
排好序以后再用:
1 2 3 4 | for (i=1;i<=n;i++) { a[node[i].index]=i; } |
这样就将原来的8 1 6 7 4转化为5 1 3 4 2,比较一下这个序列和原来的序列有什么区别?你会发现他记录了原来数组的大小和元素顺序,这两个就通过离散化很好的结合在一起了,而且将原来很大的数据压缩为一串从1开始的连续的数,大大降低了时空复杂度。
然后问题就简单了,将a数组更新到树状数组当中去,进行统计就出答案了。
开始的时候我一直都没弄清楚树状数组是怎么实现求逆序对的,后来也是看别人的博客,模拟了一下才搞懂的。
原理是什么呢?
在插入a[i]之前,我们先统计比a[i]小的数或等于a[i]的数有几个,也就是getsum(a[i]),然后再用i-getsum(a[i]),这样就得到了在他前面并且比他大的数据的个数。这样也很好理解,总数-小于或等于本身的个数=大于本身的个数,先更新再统计。
还有这题需要用long long ,开始的时候没想到害我wrong了好多次,然后静下心来算了一算发现确实要用long long,在从下往上累加的时候,最上面的那个数最大时相当于65537的平方,65537的平方就是四十多亿,int最多二十亿,妥妥的超了。用long long就过了。还有一个细节,就是当输入的序列中有数字相同时要怎么处理?首先处理这个问题时你得透彻的知道离散化的过程。
有两种解决方法:
方法一:在离散化的过程中进行处理,也就是在离散化过程中遇到两个数相同时,你得将它标记为两个数,这样就避免了相同时只计算一个数这种情况。
具体怎么来实现呢?很简单,看代码,不解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | for (i=1;i<=n;i++) { scanf ( "%d" ,&a[i].y); a[i].x=i; } //qsort(a+1,n,sizeof(a[1]),cmp); sort(a+1,a+n+1,cmp); b[1]=1; x1=1; for (i=2;i<=n;i++) { if (a[i].y==a[i-1].y) b[a[i].x]=x1; else b[a[i].x]=++x1; } for (i=1;i<=n;i++) { update(b[i],1); num+=i-sum(b[i]); } |
方法二:直接用stable_sort,即:
1 | stable_sort(node+1,node+n+1,cmp); |
现在就来讲一下sort和stable_sort的区别:
这两个函数的原理都是快速排序,时间复杂度在所有排序中最低,为O(nlog2n) ;但是stable_sort要稍微慢一点。
sort的应用;
1、可以传入两个参数;
sort(a,a+N) ,其中a是数组,a+N表示对a[0]至a[N-1]的N个数进行排序(默认从小到大排序);
2、传入三个参数;
sort(a,a+N,cmp),第三个参数是一个函数 ;
如果让函数从大到小排序,可以用如下算法实现;
bool cmp(int a,int b){return a>b};
sort(A,A+N,cmp);
而stable_sort的用法与sort一致,区别是stable_sort函数遇到两个数相等时,不对其交换顺序;这个应用在数组里面不受影响,当函数参数传入的是结构体时,会发现两者之间的明显区别。
这题如果在离散化的时候不进行处理,后面又用sort,肯定妥妥的跪了,我就是这样啊,血的教训。。。
最好的解决办法就是不管什么情况下都用stable_sort,这样就不会出现这种问题了。
下面贴一下代码:
方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> using namespace std; struct point { int x,y; }a[65540]; int b[65540],c[65540]; int cmp(point a,point b) { return a.y<b.y; } int lowbit( int x) { return x&(-x); } void update( int x, int y) { while (x<=65537) { c[x]+=y; x+=lowbit(x); } } int sum( int x) { int sum=0; while (x>0) { sum+=c[x]; x-=lowbit(x); } return sum; } int main() { int n,i,x1; __int64 num; while ( scanf ( "%d" ,&n)!=EOF) { num=0; memset (a,0, sizeof (a)); memset (b,0, sizeof (b)); memset (c,0, sizeof (c)); for (i=1;i<=n;i++) { scanf ( "%d" ,&a[i].y); a[i].x=i; } //qsort(a+1,n,sizeof(a[1]),cmp); sort(a+1,a+n+1,cmp); b[1]=1; x1=1; for (i=2;i<=n;i++) { if (a[i].y==a[i-1].y) b[a[i].x]=x1; else b[a[i].x]=++x1; } for (i=1;i<=n;i++) { update(b[i],1); num+=i-sum(b[i]); } printf ( "%I64d\n" ,num); } return 0; } |
方法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<cstdlib> #define MAX 70000 using namespace std; long long tree[MAX]; long long a[MAX]; struct Node { long long num; int index; }; Node node[MAX]; bool cmp(Node a,Node b) { return a.num<b.num; } int lowbit( int x) { return x&(-x); } void update( int x) { while (x<MAX) { tree[x]++; x+=lowbit(x); } } int getsum( int x) { int sum=0; while (x>0) { sum+=tree[x]; x-=lowbit(x); } return sum; } int main() { int n; while ( scanf ( "%d" ,&n)!=EOF) //这题不能用while(scanf("%d",&n),n),为此wrong了n次 { memset (tree,0, sizeof (tree)); int i,j,k,l; for (i=1;i<=n;++i) { scanf ( "%lld" ,&node[i].num); node[i].index=i; } stable_sort(node+1,node+n+1,cmp); // check // for(i=1;i<=n;i++) // { // printf("%d ",node[i].num); // } //对排序后的数组进行离散化 for (i=1;i<=n;i++) { a[node[i].index]=i; } // for(i=1;i<=n;i++) // { // printf("%d ",a[i]); // } //入树+统计 long long ans=0; for (i=1;i<=n;i++) { update(a[i]); // cout<<getsum(a[i])<<endl; ans+=(i-getsum(a[i])); } printf ( "%lld\n" ,ans); } return 0; } |
作者:北岛知寒
出处:https://www.cnblogs.com/crazyacking/p/3681376.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?