HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911

解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少?

因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了。

因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的:

线段树,树状数组,归并排序。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef __int64 INT;
 7 const int maxn = 100000+5;
 8
 9 struct node
10 {
11     int d,cixu;
12 }A[maxn];
13 struct Node
14 {
15     int l,r;
16     INT n;
17 }tree[4*maxn];
18 bool cmp1(node a,node b)
19 {
20     if(a.d == b.d) return a.cixu < b.cixu;
21     return a.d < b.d;
22 }
23 bool cmp2(node a,node b)
24 {
25     return a.cixu < b.cixu;
26 }
27 void build(int p)
28 {
29     if(tree[p].l == tree[p].r) return ;
30     int mid = (tree[p].l + tree[p].r) / 2;
31     tree[2*p].l = tree[p].l;
32     tree[2*p].r = mid;
33     tree[2*p].n = tree[2*p+1].n = 0;
34     tree[2*p+1].l = mid + 1;
35     tree[2*p+1].r = tree[p].r;
36     build(2*p);
37     build(2*p+1);
38 }
39 void insert(int p,int l)
40 {
41     tree[p].n++;
42     if(tree[p].l == tree[p].r) return ;
43     int mid = (tree[p].l + tree[p].r) / 2;
44     if(l <= mid) insert(2*p,l);
45     else insert(2*p+1,l);
46 }
47 INT query(int p,int l,int r)
48 {
49     if(l > r) return 0;
50     if(tree[p].l == l && tree[p].r == r)
51     return tree[p].n;
52     int mid = (tree[p].l + tree[p].r) / 2;
53     if(r <= mid) query(2*p,l,r);
54     else if(l <= mid && r > mid)
55     return query(2*p,l,mid) + query(2*p+1,mid+1,r);
56     else return query(2*p+1,l,r);
57 }
58
59 int main()
60 {
61     int n;
62     INT k;
63     while(scanf("%d%I64d",&n,&k)!=EOF)
64     {
65         tree[1].l = 1;
66         tree[1].r = n;
67         tree[1].n = 0;
68         build(1);
69         for(int i = 1;i <= n;++i)
70         {
71             scanf("%d",&A[i].d);
72             A[i].cixu = i;
73         }
74         sort(A+1,A+n+1,cmp1);
75         int f = 0,hehe = 0x7fffffff;
76         for(int i = 1;i <= n;++i)
77         {
78             if(A[i].d != hehe)
79             {
80                 hehe = A[i].d;
81                 f++;
82             }
83             A[i].d = f;
84         }
85         sort(A+1,A+n+1,cmp2);
86         INT ans = 0;
87         for(int i = 1;i <= n;++i)
88         {
89             ans += query(1,A[i].d+1,n);
90             insert(1,A[i].d);
91         }
92         ans = ans-k > 0? (ans-k) : 0;
93         printf("%I64d\n",ans);
94     }
95     return 0;
96 }

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

时间: 2024-08-08 09:39:22

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)的相关文章

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

HDU 1394 Minimum Inversion Number(线段树求逆序对数目)

HDU 1394 题意: 给一个由0~n-1组成的序列,求出该序列的所有循环同构序列中的最小逆序对数目,逆序对的两个元素可以不相邻. 思路: 这题据说可以直接暴力O(n2)可以水过.. 说一下线段树做法O(nlogn): 以这个序列来说明: 1,9,2,3,0,8,5,7,4,6 我们先假设有一个长度为n元素全为0的数组: 0,0,0,0,0,0,0,0,0,0 我们先计算所给序列的第一项1(实际上是第0项)的数字所对应位置之后所有元素的和(括号里面的数),和就是当前与这个数逆序的数的个数,这样

HDU 1394 Minimum Inversion Number(线段树求逆序数)

题目地址:HDU 1394 这题可以用线段树来求逆序数. 这题的维护信息为每个数是否已经出现.每次输入后,都从该点的值到n-1进行查询,每次发现出现了一个数,由于是从该数的后面开始找的,这个数肯定是比该数大的.那就是一对逆序数,然后逆序数+1.最后求完所有的逆序数之后,剩下的就可以递推出来了.因为假如目前的第一个数是x,那当把他放到最后面的时候,少的逆序数是本来后面比他小的数的个数.多的逆序数就是放到后面后前面比他大的数的个数.因为所有数都是从0到n-1.所以比他小的数就是x,比他大的数就是n-

HDU - 1394 Minimum Inversion Number (线段树求逆序数)

Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seq

HDU 1394 Minimum Inversion Number(线段树求逆序数啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an,

HDU 1394- Minimum Inversion Number(线段树求逆序数)

Minimum Inversion Number Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1394 Appoint description:  System Crawler  (2015-04-13) Description The inversion number of a given number sequence a1, a

hdu 1394 Minimum Inversion Number 【线段树求逆序数】

之前写过树状数组的,再用线段树写一下--- 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<vector> 7 using namespace std; 8 #define lp (p << 1) 9 #define rp (p << 1

HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)

题意:给定一个序列,求分别将前m个数移到序列最后所得到的序列中,最小的逆序数. 分析:m范围为1~n,可得n个序列,求n个序列中最小的逆序数. 1.将序列从头到尾扫一遍,用query求每个数字之前有多少个大于该数字的数,方法如下. (1)将已经扫过的数字所对应的位置标记,通过query求该数字之后有多少个数被标记过 (2)该数字之后所有被标记的数字,都是在该数字之前出现过的(i<j),而这些数字又大于该数字(ai>aj),因此该数字之后所有的标记和就是该数字之前比该数字大的数的个数. 2.su

hdu 4911 求逆序对数+树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4911 给定一个序列,有k次机会交换相邻两个位置的数,问说最后序列的逆序对数最少为多少. 实际上每交换一次能且只能减少一个逆序对,所以问题转换成如何求逆序对数. 归并排序或者树状数组都可搞 树状数组: 先按大小排序后分别标号,然后就变成了求1~n的序列的逆序数,每个分别查询出比他小的用i减,在把他的值插入即可 #include <cstdio> #include <cstdlib> #includ