模板——权值线段树(逆序对)

Ultra-QuickSort

Time Limit: 7000MS   Memory Limit: 65536K
Total Submissions: 62455   Accepted: 23259

Description

In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence 
9 1 0 5 4 ,
Ultra-QuickSort produces the output 
0 1 4 5 9 .
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

Sample Input

5
9
1
0
5
4
3
1
2
3
0

Sample Output

6
0

Source

Waterloo local 2005.02.05

大意就是给若干个长度为n的序列求逆序对个数。

权值线段树维护的是某个值出现的次数,所以一开始是空树(换句话说就不用建树了2333),然后不断按顺序从左到右插入点,很显然每插入一个点就记录比这个点大的个数即为此时的逆序对个数,然后累计即可。

因为数值可达9亿9千9百9十9万9千9百9十9,但个数最多只有500000,所以先离散后插入。(ans要long long!!!)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 using namespace std;
 6 int n,a[500005],tmp[500005],size;
 7 long long ans,cnt[2000005];
 8 void init(int root,int l,int r,int x){
 9     if ((l==r)&&(l==x)) {
10         cnt[root]++;
11         return;
12     }
13     int mid=(l+r)>>1;
14     if (x<=mid) init(root<<1,l,mid,x);
15     else if (x>mid) init(root<<1|1,mid+1,r,x);
16     cnt[root]=cnt[root<<1]+cnt[root<<1|1];
17 }
18 long long sum(int root,int l,int r,int x,int y){
19     if ((x<=l)&&(y>=r)) return cnt[root];
20     long long anss=0;
21     int mid=(l+r)>>1;
22     if (x<=mid) anss+=sum(root<<1,l,mid,x,y);
23     if (y>mid) anss+=sum(root<<1|1,mid+1,r,x,y);
24     return anss;
25 }
26 int main(){
27     scanf("%d",&n);
28     while (n){
29         memset(a,0,sizeof(a));
30         memset(cnt,0,sizeof(cnt));
31         memset(tmp,0,sizeof(tmp));
32         size=0;
33         ans=0;
34         for (int i=1;i<=n;i++){
35          scanf("%d",&a[i]);
36          tmp[i]=a[i];
37         }
38         sort(tmp+1,tmp+1+n);
39         size=unique(tmp+1,tmp+1+n)-(tmp+1);
40         for (int i=1;i<=n;i++)
41          a[i]=lower_bound(tmp+1,tmp+1+size,a[i])-(tmp+1)+1;
42         for (int i=1;i<=n;i++){
43          init(1,1,n,a[i]);
44          ans+=sum(1,1,n,a[i]+1,n);
45         }
46         printf("%lld\n",ans);
47         scanf("%d",&n);
48     }
49     return 0;
50 }

神奇的代码

时间: 2024-10-08 23:00:44

模板——权值线段树(逆序对)的相关文章

权值线段树求逆序对问题

我们都知道,求逆序对数量可以用归并排序解决.但是用归并排序只能解决静态的序列问题,没有扩展的区间.因此就有了用权值线段树求逆序对的方法. 1 #include<iostream> 2 #include<iomanip> 3 #include<ctime> 4 #include<climits> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #inc

【权值线段树】离散化介绍 (+利用 线段树 求逆序对)

先介绍一下离散化 桶排大家应该知道,就是开一个数组(下标为数值,记录了该数值的出现次数)然后遍历过去如果出现次数不为零,那就输出这些数字,理论时间复杂度可以达到O(N)但是由于内存限制,不能开很大的数组. 然而 如果某个数列中的数字不要求大小确定,只要求这些数字有相对的大小就够了的话,离散化就有了用武之地 举个例子:数列 3 8 7 5 2000000000000000 我们发现有几个数之间差距很大,但是我们用不到数值的大小,只要求相对大小,那怎么办呢? 观察下面的数列: 1 4 3 2 5 真

权值线段树&amp;&amp;线段树合并

权值线段树 所谓权值线段树,就是一种维护值而非下标的线段树,我个人倾向于称呼它为值域线段树. 举个栗子:对于一个给定的数组,普通线段树可以维护某个子数组中数的和,而权值线段树可以维护某个区间内数组元素出现的次数. 在实现上,由于值域范围通常较大,权值线段树会采用离散化或动态开点的策略优化空间. 更新操作: 更新的时候,我们向线段树中插入一个值v,那么所有包含v的区间值都需要+1.(每个节点维护对应区间中出现了多少个数) int update (long long v,long long l,lo

【bzoj3702】二叉树 权值线段树

神奇的解法 对于每个节点,建出权值线段树 每次查询右子树的权值线段树和左子树的权值线段树,左子树中比右子树小的有多少?右子树比左子树小的有多少?(分别对应不交换的逆序对和交换的逆序对) 将左子树和右子树的权值线段树合并 递归进行这个操作 感觉复杂度很不靠谱,于是想证明一下复杂度 最开始权值线段树共O(nlogn)个节点,最后共O(n)个节点 每次合并两棵树的每个节点都要访问一遍,所以每个节点好像是要访问O(dep[i])次? 但是,合并两棵树后,有些重复的节点被合并到了一起 所以好像有些节点又没

权值线段树2

今天给大家带来的是求逆序对个数. 我会归并! 当然,还是用权值线段树解决 题目链接:逆序对1或逆序对2 都是板子,一摸一样,双倍积分 首先,逆序对是什么? 对于给定的一段正整数序列,逆序对就是序列中ai>aj且i< j的有序对 通俗来讲,就是有一列数,如果有这样两个数m,n满足 m在n前面 m比n大 那么m,n就是一对逆序对,现在我们要求的就是这种逆序对个数. 我们能够想到这样一种方法: 读入一个数,查找之前读入的数中比它大的数的个数,累加答案 把它丢进某种数据结构中 而这"某种数据

Luogu P1637 三元上升子序列【权值线段树】By cellur925

题目传送门 emmm..不开结构体的线段树真香! 首先我们知道"三元上升子序列"的个数就是对于序列中的每个数,**它左边比他小的数*它右边比他大的数**.但是如何快速求出这两个数? 我们用到权值线段树来维护.一般我们的线段树都是以下标延伸的,但是这里我们用的是权值,一般需要离散化,效果相当于一个桶. 这部分讲解请移步绝世好文 第一次我们从\(1\)~\(n\)循环是为了找它左边的,而找比他小的值是在线段树的\(1\)~\(seq[i]-1\)中找.第二次我们从\(n\)~\(1\)循环

20190616 权值线段树

线段树咕咕咕 我来写一个好写的权值线段树的解析吧 权值线段树是什么 线段树每个点维护的是点的值,而权值线段树叶子点维护的是一个数出现的次数,父节点维护的是它代表的区间里的数出现的次数的和 权值线段树基本操作 其实权值线段树的基本操作与线段树没有太大的不同 建树 注意:由于一个叶子节点代表一个数出现的次数,所以,在数的范围非常大时,我们常常需要把这些数排个序,按排序重新赋值(因为权值线段树里数本身的值不重要,我们只需要关注它出现的次数),这也叫做离散化 void build(int l,int r

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前