2016多校训练#5 1012 HDU 5792 树状数组 代码详解
2016-08-03 20:37
405 查看
World is Exploding
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 666 Accepted Submission(s): 315
Problem Description
Given a sequence A with length n,count how many quadruple (a,b,c,d) satisfies: a≠b≠c≠d,1≤a<b≤n,1≤c<d≤n,Aa<Ab,Ac>Ad.
Input
The input consists of multiple test cases.
Each test case begin with an integer n in a single line.
The next line contains n integers A1,A2⋯An.
1≤n≤50000
0≤Ai≤1e9
Output
For each test case,output a line contains an integer.
Sample Input
4
2 4 1 3
4
1 2 3 4
Sample Output
1
0
从上面的题目描述来看,本题与2016多校#4的最后一道题有异曲同工之妙,都是寻找数组中间某一个元素的逆序对,然而不同的是,本题的数据范围更大,数据也并不是像上次的题目一样是连续的1到n,而是离散的。这个时候,如果使用线段树来进行查询的话肯定很复杂(反正楼主弱,不会离散化的线段树),楼主第一次写树状数组,借鉴了其他高手的AC代码进行注释解释,以下,方便初学者看懂:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<vector> #include<map> #include<algorithm> #include<queue> #include<stack>//树状数组 using namespace std; typedef long long ll; #define N 50010 vector<int>v; int num , vis ; int pos , val ; int e1 , e2 ; int lg , rg , ls , rs ;//左小 右小 左大 右大 int c[2] ; int n; bool cmp1(int a, int b) { if(val[a] == val[b]) return pos[a] < pos[b]; return val[a] < val[b]; } bool cmp2(int a, int b) { if(val[a] == val[b]) return pos[a] < pos[b]; return val[a] > val[b]; } int lowbit(int x)//求x为二进制表示的最低位 0的个数 { return x&(-x);//k的值就表示子树的个数,子树即为树状数组的元素 } void update(int kind, int x, int val)//更新这个节点到祖先的所有节点的值 { for(; x <= n; x += lowbit(x)) c[kind][x] += val; } //i的父节点为p,则p = i + lowbit(i); int query(int kind, int x)//树状数组求前N个元素的和 { if(x == 0) return 0; int sum = 0; for(; x > 0; x -= lowbit(x)) sum += c[kind][x]; return sum; } int main() { while(~scanf("%d", &n)) { memset(vis, 0, sizeof(vis)); v.clear(); for(int i = 1; i <= n; i++) { pos[i] = i; scanf("%d", &val[i]); v.push_back(val[i]);//将读进去的元素放到向量中 e1[i] = e2[i] = i; } sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());//去除数组中相邻重复元素 for(int i = 1; i <= n; i++) { val[i] = 1+(lower_bound(v.begin(), v.end(), val[i])-v.begin());//求每个元素在排序后数组中的位置 //即 第i个元素的大小顺序位置 } for(int i = 1; i <= n; i++) { num[i] = vis[val[i]];//在第i个元素之前等于这个元素的个数 vis[val[i]]++; } sort(e1+1, e1+n+1, cmp1);//将数组元素的位置按数组元素值升序排序 sort(e2+1, e2+n+1, cmp2);//将数组元素的位置按数组元素值降序排序 memset(c, 0, sizeof(c)); ll all1 = 0, all2 = 0;//表示所有元素之前小 和 之后小的元素之和 for(int i = 1; i <= n; i++) { int e = e1[i];//数值第i小的元素的位置 lg[e] = query(0, e-1)-num[e];//查询原数组第e个元素左边比它小的元素个数(删掉前面等于的元素) update(0, e, 1); all1 += lg[e]; e = e2[i]; rg[e] = query(1, e-1)-num[e];//查询原数组第e个元素右边比它小的元素个数(删掉前面等于的元素) update(1, e, 1); all2 += rg[e]; } memset(c, 0, sizeof(c)); for(int i = 1; i <= n; i++) { int e = e1[i]; ls[e] = query(0, n)-query(0, e);//左边比他大的元素个数 update(0, e, 1); e = e2[i]; rs[e] = query(1, n)-query(1, e);//右边比他大的元素个数 update(1, e, 1); } ll ans = all1*all2; for(int i = 1; i <= n; i++) { ans -= (ll)rs[i]*rg[i]+(ll)lg[i]*ls[i]+(ll)lg[i]*rg[i]+(ll)ls[i]*rs[i]; } printf("%lld\n", ans); } return 0; }
原作者不详,特此鸣谢!
相关文章推荐
- (HDU 5792)World is Exploding <树状数组+去重> 多校训练5
- 2016 Multi-University Training Contest 4 1012 hdu 5775(树状数组)
- (HDU 5775)Bubble Sort <树状数组> 多校训练4
- 树状数组 ( 求逆序数 )——Bubble Sort ( HDU 5775 ) ( 2016 Multi-University Training Contest 4 1012 )
- 树状数组(代码详解)
- HDU 4920(杭电多校训练#5 1010 题) Matrix multiplication(不知道该挂个什么帽子。。。)
- HDU 5792 树状数组
- hdu 5792 World is Exploding 树状数组
- HDU 4630 No Pain No Game(2013多校3 1010题 离线处理+树状数组求最值)
- 树状数组题目详解 HDU 1166 HDU 1541
- (多校第四场1012)HDU5763 Bubble Sort (树状数组)
- 2016 Multi-University Training Contest 4 - 1012(树状数组求逆序数 )
- HDU 5792 World is Exploding (树状数组)
- hdu 4970 Killing Monsters(数组的巧妙运用) 2014多校训练第9场
- 2016多校训练Contest4: 1012 Bubble Sort hdu5775
- hdu 4325 Flowers 2012多校1006题 树状数组解法
- hdu 多校contest4 group(树状数组)
- 多校第七场 1004 hdu 5372 Segment Game(树状数组)
- HDU_5372 树状数组 (2015多校第7场1004)
- HDU 5792 World is Exploding (树状数组逆序对)