NOIP 2013 Senior 2
来源:互联网 发布:淘宝评分低怎么办 编辑:程序博客网 时间:2024/05/29 11:03
涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相 同,两列火柴之间的距离定义为: ,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度。
每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要 交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。
Input
共三行,第一行包含一个整数 n,表示每盒中火柴的数目。
第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。
第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。
Output
输出共一行,包含一个整数,表示最少交换次数对 99,999,997 取模的结果。
Sample Input
输入1:
 4
2 3 1 4
3 2 1 4
输入2:
 4
1 3 4 2
1 7 2 4
Sample Output
输出1:
1
样例1说明:
最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。
输出2:
2
样例2说明:
最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。
Data Constraint
对于 10%的数据, 1 ≤ n ≤ 10;
对于 30%的数据,1 ≤ n ≤ 100;
对于 60%的数据,1 ≤ n ≤ 1,000;
对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 2^31 − 1。
首先,我们看数据规模,发现这个数据规模适合
发现,如果可以交换其中一列火柴,那么交换另一列火柴也可以用相同的次数达到同样的效果,因此我们可以只操作一列。现在的问题就是,怎样的最终状态是最佳的。
观察
,发现原式可以展开变为D=Σni=1(ai+bi)2 。D=Σni=1(a2i+b2i)−2∗Σni=1(ai∗bi) 因此相当于要使
最大,这时可以使用排序不等式。即正序和(Σni=1(ai∗bi) ai∗bj 的和)>乱序和>逆序和,因此只需要让两列火柴中每对火柴排序后对应的序号相等就可以了。
为了防止间接排序出现问题,这里使用直接排序:
struct info{ INT height; INT index; INT sortIndex;} infos[2][maxn];
首先,根据height
关键字进行排序,然后记录每根火柴排序后的序号sortIndex
,再根据原序号index
排序还原序列。然后,用第一列火柴的数据记录每个排序后的序号应该放的位置。用这个“位置序号”去替换第二列的sortIndex
得到一个新序列。这个新序列的逆序数便是答案。
例如,两列火柴为
1 3 2 47 2 5 4
它们对应的
sortIndex
为1 3 2 44 1 3 2
则1该放的位置为1,2该放的位置为3,3该放的位置为2,4该放的位置为4。
用该放的位置去替换排序序号
//第二列4 1 2 3
最后求出这个序列的逆序数便是答案。
现在的问题就成了,如何求逆序数。逆序数可以使用老生常谈的归并排序来求,在考试时我也使用的是归并排序。归并排序求逆序数的写法不再解释。
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef long long INT;inline INT readIn(){ bool minus = false; INT a = 0; char ch = getchar(); while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a *= 10; a += ch; a -= '0'; ch = getchar(); } if (minus) a = -a; return a;}const INT mod = 99999997;const INT maxn = 100005;INT n;struct info{ INT height; INT index; INT sortIndex;} infos[2][maxn];bool comp1(const info& a, const info& b){ return a.height < b.height;}bool comp2(const info& a, const info& b){ return a.index < b.index;}INT sequence[2][maxn];INT temp[maxn];INT ans;void mergesort(INT x, INT y){ if (y - x == 1) return; INT mid = (x + y) / 2; mergesort(x, mid); mergesort(mid, y); INT i = x; INT j = mid; INT k = x; while (i < mid || j < y) { if (j >= y || i < mid && sequence[1][i] <= sequence[1][j]) { temp[k++] = sequence[1][i++]; } else { ans += mid - i; temp[k++] = sequence[1][j++]; } } for (i = x; i < y; i++) { sequence[1][i] = temp[i]; }}void run(){ n = readIn(); for (int i = 0; i < n; i++) { infos[0][i].height = readIn(); infos[0][i].index = i; } for (int i = 0; i < n; i++) { infos[1][i].height = readIn(); infos[1][i].index = i; } std::sort(infos[0], infos[0] + n, comp1); for (int i = 0; i < n; i++) { infos[0][i].sortIndex = i; } std::sort(infos[0], infos[0] + n, comp2); std::sort(infos[1], infos[1] + n, comp1); for (int i = 0; i < n; i++) { infos[1][i].sortIndex = i; } std::sort(infos[1], infos[1] + n, comp2); for (int i = 0; i < n; i++) { sequence[0][infos[0][i].sortIndex] = i; } for (int i = 0; i < n; i++) { sequence[1][i] = sequence[0][infos[1][i].sortIndex]; } mergesort(0, n); cout << ans % mod << endl;}int main(){ run(); return 0;}
其实,要获取那个序列还有更简单的方法。我们直接建立index[0]到index[1]的映射就可以了。这里我们设了index[i]=i,所以可以这样赋值:
sequence[index1[i]] = index2[i];
另附间接排序代码。这次重写了一次间接排序,又没有出错,但是一定要明确排序后得到的是什么。毫无疑问,间接排序还要快一些。
//Indirect sort#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef long long INT;inline INT readIn(){ bool minus = false; INT a = 0; char ch = getchar(); while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a *= 10; a += ch; a -= '0'; ch = getchar(); } if (minus) a = -a; return a;}const INT mod = 99999997;const INT maxn = 100005;INT n;INT height1[maxn];INT index1[maxn];INT height2[maxn];INT index2[maxn];bool comp1(const INT& a, const INT& b){ return height1[a] < height1[b];}bool comp2(const INT& a, const INT& b){ return height2[a] < height2[b];}INT sequence[maxn];INT temp[maxn];INT ans;void mergesort(INT x, INT y){ if (y - x == 1) return; INT mid = (x + y) / 2; mergesort(x, mid); mergesort(mid, y); INT i = x; INT j = mid; INT k = x; while (i < mid || j < y) { if (j >= y || i < mid && sequence[i] <= sequence[j]) { temp[k++] = sequence[i++]; } else { ans += mid - i; temp[k++] = sequence[j++]; } } for (i = x; i < y; i++) { sequence[i] = temp[i]; }}void run(){ n = readIn(); for (int i = 0; i < n; i++) { height1[i] = readIn(); index1[i] = i; } for (int i = 0; i < n; i++) { height2[i] = readIn(); index2[i] = i; } std::sort(index1, index1 + n, comp1); std::sort(index2, index2 + n, comp2); for (int i = 0; i < n; i++) { sequence[index1[i]] = index2[i]; } mergesort(0, n); cout << ans % mod << endl;}int main(){ run(); return 0;}
除了归并排序可以求逆序数,还可以使用树状数组求逆序数。这还是第一次见,所以给出解析。
树状数组求逆序数
逆序数定义为前面的数大于后面的数的个数。求逆序数时,往往不是拿到一个数后往后找比自己小的数,而是往前找比自己大的数,因为前面的数是之前读入的,经过一些处理后就能快速求逆序数了。
树状数组便能成为一种处理方式。我们可以定义
如果数字比较小并且没有负数,那么这是一种相当高效的算法。但是往往数据都是
可以先对数据排序,得出序号来,然后将序号看做原数。这样的话树状数组的大小就只与数据规模有关了。如果有相同的数,则需要将他们编为相同的号,但这就有些麻烦了,不如直接用归并排序来求。所以建议:当没有重复数据或数据较小且为正数时使用树状数组来求逆序数,有重复数据时使用归并排序来求逆序数。在适当条件下,树状数组要快一点。
参考代码
//Indirect sort + BIT#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>#include <bitset>using std::cin;using std::cout;using std::endl;typedef long long INT;inline INT readIn(){ bool minus = false; INT a = 0; char ch = getchar(); while (!(ch == '-' || ch >= '0' && ch <= '9')) ch = getchar(); if (ch == '-') { minus = true; ch = getchar(); } while (ch >= '0' && ch <= '9') { a *= 10; a += ch; a -= '0'; ch = getchar(); } if (minus) a = -a; return a;}const INT mod = 99999997;const INT maxn = 100005;INT n;INT height1[maxn];INT index1[maxn];INT height2[maxn];INT index2[maxn];bool comp1(const INT& a, const INT& b){ return height1[a] < height1[b];}bool comp2(const INT& a, const INT& b){ return height2[a] < height2[b];}INT sequence[maxn];INT temp[maxn];inline INT lowbit(INT x){ return x & -x;}INT getInv(){ INT ans = 0; std::vector<INT> BIT(n + 1); for(int i = 0; i < n; i++) { INT x; x = sequence[i] + 1; //必须从1开始,所以重编码 while(x <= n) { BIT[x]++; x += lowbit(x); } INT count_ = 0; x = sequence[i] + 1; while(x > 0) { count_ += BIT[x]; x -= lowbit(x); } ans += i + 1 - count_; } return ans;}void run(){ n = readIn(); for (int i = 0; i < n; i++) { height1[i] = readIn(); index1[i] = i; } for (int i = 0; i < n; i++) { height2[i] = readIn(); index2[i] = i; } std::sort(index1, index1 + n, comp1); std::sort(index2, index2 + n, comp2); for (int i = 0; i < n; i++) { sequence[index1[i]] = index2[i]; } cout << getInv() % mod << endl;}int main(){ run(); return 0;}
- NOIP 2013 Senior 2
- NOIP 2013 Senior 3
- NOIP 2013 Senior 4
- NOIP 2013 Senior 5
- NOIP 2013 Senior 6
- NOIP 2011 Senior 2
- NOIP 2012 Senior 2
- NOIP 2015 Senior 2
- NOIP 2014 Senior 2
- NOIP 2016 Senior 2
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2011 Senior 4
- NOIP 2011 Senior 5
- NOIP 2011 Senior 6
- NOIP 2012 Senior 5
- FFMPEG参数中文说明
- 自己动手搭建React开发环境之五Conclusion
- MySql 删除数据库出错:Can't rmdir '.\test\', errno: 17
- linux kernel 中断子系统之(一)-- ARM GIC 硬件
- 1188-C语言实验-各位数字之和排序
- NOIP 2013 Senior 2
- GC判断对象是否存活
- HEVC(H.265)的技术亮点
- LA 2963
- 比较排序算法总结
- pom.xml引入jar包:jar包artifactId查询方法
- 日期加月份
- imx6设备树pinctrl解析
- 剑指offer面试题55 字符流中第一个不重复的字符(Java实现)