NOIP2013D1T2 火柴排队 题解
2016-11-18 00:53
120 查看
(题目描述略)
对于距离最小,其贪心策略为将两序列升序排序后比较。以下为简易的证明:
设对任意 a1 < a2,b1 < b2,
第一种排布是 ..a1a2..,..b1b2..,此时距离为 (a1 - b1) ^ 2 + (a2 - b2) ^ 2 (1),
第二种排布是 ..a1a2..,..b2b1..,此时距离为 (a1 - b2) ^ 2 + (a2 - b1) ^ 2 (2),
用(1)式减去(2)式,得差为 2(a1 - a2)(b2 - b1) < 0,
故(1)<(2),贪心策略得证。
于是,原题转化为,求最少的相邻交换次数使得两序列的大小顺序相同。考虑到两序列的大小顺序之间的关系不方便确定,我们可以用离散化的方法对序列预处理,再求其逆序对即可。
将序列预处理的过程如下:首先现将第一个序列中的值离散到整数区间 [1, n] 中,记为 order1[1..n],其中 order1[i] 表示第一个序列中下标为 i 的数值是序列中第 order1[i] 小的数值;再将第二个序列离散化,记为 order2[1..n],其中 order2[i] 表示第二个序列中第 i 小的数值的下标。
所以,我们可以发现,第一个序列中下标为 i 的数值对应第二个序列中下标为 order2[order1[i]] 的数值。于是,原题再次转化为,求最少的相邻交换次数使得序列 order2[order1[1..n]] 变成升序序列。对序列 order2[order1[1..n]] 求逆序对即为结果。
关于逆序对的求法,常见的有树状数组和归并排序两种。对于树状数组而言,我们令 i 升序依次在树状数组下标为 order2[order1[i]] 中插入 i,每加入一个数统计当前状态下在其前面的数,即统计 j 的个数使得 i > j 且 order2[order1[i]] > order2[order1[j]],在用 i 减去统计结果即为所有以 i 为起始的逆序对个数。求和即可。
对于归并排序而言,在每次对序列 order2[order1[i]] 归并的过程中,设前半段扫描指针 i,后半段扫描指针 j,后半段起始指针 m,若扫描中发现 order2[order1[i]] > order2[order1[j]],则逆序对计数器加上 m - i。因为此时对于任意 i < k < m,均有 order2[order1[k - 1]] < order2[order1[k]],所以对于任意 i ≤ k < m,均有 order2[order1[k]] > order2[order1[j]]。一旦发现,扫描指针 j 右移,故不会重复计算。每次归并计算出当前两段有序子序列之间的逆序对个数并将两段有序子序列合并成一段新的有序有序子序列,这样便能统计出所有原序列中的逆序对。
这两种算法的时间效率类似,均为 O(nlogn) 级别的算法;预处理中的离散化操作因用快排故时间效率也为 O(nlogn)。于是,总算法时间复杂度为 O(nlogn)。
代码如下:(树状数组版本)
不得不说,本题离散化的思想有些复杂,但保持思路清晰仍然可解。当然,本题解只给出了树状数组版本的代码实现,对于归并排序版本的代码实现请读者自行探究。
对于距离最小,其贪心策略为将两序列升序排序后比较。以下为简易的证明:
设对任意 a1 < a2,b1 < b2,
第一种排布是 ..a1a2..,..b1b2..,此时距离为 (a1 - b1) ^ 2 + (a2 - b2) ^ 2 (1),
第二种排布是 ..a1a2..,..b2b1..,此时距离为 (a1 - b2) ^ 2 + (a2 - b1) ^ 2 (2),
用(1)式减去(2)式,得差为 2(a1 - a2)(b2 - b1) < 0,
故(1)<(2),贪心策略得证。
于是,原题转化为,求最少的相邻交换次数使得两序列的大小顺序相同。考虑到两序列的大小顺序之间的关系不方便确定,我们可以用离散化的方法对序列预处理,再求其逆序对即可。
将序列预处理的过程如下:首先现将第一个序列中的值离散到整数区间 [1, n] 中,记为 order1[1..n],其中 order1[i] 表示第一个序列中下标为 i 的数值是序列中第 order1[i] 小的数值;再将第二个序列离散化,记为 order2[1..n],其中 order2[i] 表示第二个序列中第 i 小的数值的下标。
所以,我们可以发现,第一个序列中下标为 i 的数值对应第二个序列中下标为 order2[order1[i]] 的数值。于是,原题再次转化为,求最少的相邻交换次数使得序列 order2[order1[1..n]] 变成升序序列。对序列 order2[order1[1..n]] 求逆序对即为结果。
关于逆序对的求法,常见的有树状数组和归并排序两种。对于树状数组而言,我们令 i 升序依次在树状数组下标为 order2[order1[i]] 中插入 i,每加入一个数统计当前状态下在其前面的数,即统计 j 的个数使得 i > j 且 order2[order1[i]] > order2[order1[j]],在用 i 减去统计结果即为所有以 i 为起始的逆序对个数。求和即可。
对于归并排序而言,在每次对序列 order2[order1[i]] 归并的过程中,设前半段扫描指针 i,后半段扫描指针 j,后半段起始指针 m,若扫描中发现 order2[order1[i]] > order2[order1[j]],则逆序对计数器加上 m - i。因为此时对于任意 i < k < m,均有 order2[order1[k - 1]] < order2[order1[k]],所以对于任意 i ≤ k < m,均有 order2[order1[k]] > order2[order1[j]]。一旦发现,扫描指针 j 右移,故不会重复计算。每次归并计算出当前两段有序子序列之间的逆序对个数并将两段有序子序列合并成一段新的有序有序子序列,这样便能统计出所有原序列中的逆序对。
这两种算法的时间效率类似,均为 O(nlogn) 级别的算法;预处理中的离散化操作因用快排故时间效率也为 O(nlogn)。于是,总算法时间复杂度为 O(nlogn)。
代码如下:(树状数组版本)
#include"stdio.h" #include"stdlib.h" #include"string.h" #define lowbit(x) ((x)&-(x)) int high[100005],sum[100005],order1[100005],order2[100005]; int cmp(const void *p,const void *q) { return high[*(int *)p]>high[*(int *)q]?1:-1; } int main() { freopen("match.in","r",stdin); freopen("match.out","w",stdout); int ans=0,n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&high[i]), order2[i]=i; qsort(order2+1,n,sizeof(int),cmp); for(int i=1;i<=n;i++) order1[order2[i]]=i; for(int i=1;i<=n;i++) scanf("%d",&high[i]), order2[i]=i; qsort(order2+1,n,sizeof(int),cmp); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { for(int j=order2[order1[i]];j<=n;j+=lowbit(j)) sum[j]++; ans+=i; for(int j=order2[order1[i]];j>0;j-=lowbit(j)) ans-=sum[j]; ans%=99999997; } printf("%d",ans); return 0; }
不得不说,本题离散化的思想有些复杂,但保持思路清晰仍然可解。当然,本题解只给出了树状数组版本的代码实现,对于归并排序版本的代码实现请读者自行探究。
相关文章推荐
- 3286 火柴排队 2013年NOIP全国联赛提高组
- 【NOIP2013提高组】火柴排队
- 洛谷P1966 火柴排队[NOIP提高组2013]
- NOIP2013 火柴排队 解题报告(贪心+逆序对)
- noip2013火柴排队
- codevs 3286 火柴排队
- NOIP2013火柴排队
- Noip2013 Day1 T2 火柴排队(归并排序/树状数组 求逆序对)
- Codevs 火柴排队
- NOIP 2013 火柴排队
- 火柴排队(codevs 3286)题解
- rqnoj(PID737 / 火柴排队 match)
- NOIP 2013 - 提高组 火柴排队 归并排序+逆序对 重庆一中高2018级竞赛班第六次测试 2016.7.31 Problem 4
- 【NOIP2013】火柴排队(逆序对)
- [noip2013tg] 火柴排队
- [NOIP2013] 火柴排队 离散化 树状数组 逆序对
- [NOIP2013]火柴排队
- 火柴排队 2013年NOIP全国联赛提高组
- NOIP2013提高组 火柴排队(重庆一中高2018级信息学竞赛测验6) 解题报告
- 【NOIP2013】火柴排队