P1966题解
题意简化
给定一个正整数
暴力简述
注意到,交换
正解思路及部分代码片段
首先,见【暴力简述】,可以固定一个数组,交换另一个数组中的元素,尽可能的最小化
在这里解释一下:若有一个长度为
(上述公式中
那么,已知一个序列,该如何求呢?暴力当然很简单,双层循环加判断,统计一下就行了。然而时间复杂度是 ,但是我不会)。若需求序列
其中,
(上述公式中
然而,根据主定理,若有一函数
(表示一个规模为
则其时间复杂度的计算方式为:
此时,
如果在合并的时候(
在合并的时候,我们需要做两件事:将两个单调不下降区间(
首先,先看第一件事,将两个单调不下降的区间合并成一个单调不下降的大区间,很容易地就能够想到双指针。下面提供一个例子(片段):
int p1 = l1, p2 = l2, p3 = l1;
while(p1 <= r1 && p2 <= r2) {
if(a[p1] > a[p2]) b[p3++] = a[p2++];
else b[p3++] = a[p1++];
}
while(p1 <= r1) b[p3++] = a[p1++];
while(p2 <= r2) b[p3++] = a[p2++];
for(int i = l1; i <= r2; i ++) a[i] = b[i];
上面的代码片段完成了将
第一件事完成了。那么,如何在这个过程中,加入第二件事:统计逆序对数量(
首先,因为
实际上就是在上面的代码片段中加入一个语句,变成这样:
int p1 = l1, p2 = l2, p3 = l1;
while(p1 <= r1 && p2 <= r2) {
if(a[p1] > a[p2]) {
b[p3++] = a[p2++];
ans += (r1 + 1 - p1);
}
else b[p3++] = a[p1++];
}
while(p1 <= r1) b[p3++] = a[p1++];
while(p2 <= r2) b[p3++] = a[p2++];
for(int i = l1; i <= r2; i ++) a[i] = b[i];
只需要在这个基础上对
好了,就这样,我们把
再根据主定理,此时,
如果想要练习的话,可以做一做 模板题-逆序对。
好了,讲了这么多,到底该如何用进这道题目中去呢?首先,肯定要得到
第一种方式:将
struct node {
int Val, Index;
}a[100010], b[100010];
然后再用一个函数作为排序(快速排序)函数的第三个参数:
bool cmp(node u, node v) {
return (u.Val < v.Val);
}
用的时候就这么用:
sort(a + 1, a + n + 1, cmp);
sort(b + 1, b + n + 1, cmp);
还有第二种方式:用
定义很普通:
int a[100010], b[100010], p[100010], q[100010];
记得初始化:
for(int i = 1; i <= n; i ++) {
p[i] = q[i] = i;
}
函数这么写(两个):
bool cmpa(int u, int v) {
return (a[u] < a[v]);
}
bool cmpb(int u, int v) {
return (b[u] < b[v]);
}
用的时候就这么用:
sort(p + 1, p + n + 1, cmpa);
sort(q + 1, q + n + 1, cmpb);
反正不管怎么样,排完序之后(离散化)都会得到两个下标数组(a[i].Index 和 b[i].Index、p[i] 和 q[i])。接下来,再定义一个新数组
第一种方式:
for(int i = 1; i <= n; i ++) {
c[a[i].Index] = b[i].Index;
// c[b[i].Index] = a[i].Index;
}
第二种方式:
for(int i = 1; i <= n; i ++) {
c[p[i]] = q[i];
// c[q[i]] = p[i];
}
反正不管怎么样,现在都会得到一个
就这样,这道看似很难的题就被解决了。
代码
最后再补充一点,再之前讲合并函数的时候,形参一共有四个。但是不难发现,实际上并不需要四个,可以只有三个甚至两个,我个人习惯用三个形参的方式来实现,这并不代表其它方式就不行了,仅限个人习惯。好了,上代码。
第一种实现方式:
#include <bits/stdc++.h>
using namespace std;
const int Mod = 99999997;
int n, ans, c[100010], d[100010];
struct node {
int Val, Index;
}a[100010], b[100010];
bool cmp(node u, node v) {
return (u.Val < v.Val);
}
void Merge(int l, int mid, int r) {
int p1 = l, p2 = mid + 1, p3 = l;
while(p1 <= mid && p2 <= r) {
if(c[p1] > c[p2]) {
d[p3++] = c[p2++];
ans = (ans + mid + 1 - p1) % Mod;
}
else d[p3++] = c[p1++];
}
while(p1 <= mid) d[p3++] = c[p1++];
while(p2 <= r) d[p3++] = c[p2++];
for(int i = l; i <= r; i ++) {
c[i] = d[i];
}
}
void Sort(int l, int r) {
if(l == r) return;
int mid = (l + r) >> 1;
Sort(l, mid);
Sort(mid + 1, r);
Merge(l, mid, r);
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i].Val);
a[i].Index = i;
}
for(int i = 1; i <= n; i ++) {
scanf("%d", &b[i].Val);
b[i].Index = i;
}
sort(a + 1, a + n + 1, cmp);
sort(b + 1, b + n + 1, cmp);
for(int i = 1; i <= n; i ++) {
c[a[i].Index] = b[i].Index;
}
Sort(1, n);
printf("%d", ans);
return 0;
}
第二种实现方式:
#include <bits/stdc++.h>
using namespace std;
const int Mod = 99999997;
int n, ans, a[100010], b[100010], c[100010], d[100010], p[100010], q[100010];
bool cmpa(int u, int v) {
return (a[u] < a[v]);
}
bool cmpb(int u, int v) {
return (b[u] < b[v]);
}
void Merge(int l, int mid, int r) {
int p1 = l, p2 = mid + 1, p3 = l;
while(p1 <= mid && p2 <= r) {
if(c[p1] > c[p2]) {
d[p3++] = c[p2++];
ans = (ans + mid + 1 - p1) % Mod;
}
else d[p3++] = c[p1++];
}
while(p1 <= mid) d[p3++] = c[p1++];
while(p2 <= r) d[p3++] = c[p2++];
for(int i = l; i <= r; i ++) {
c[i] = d[i];
}
}
void Sort(int l, int r) {
if(l == r) return;
int mid = (l + r) >> 1;
Sort(l, mid);
Sort(mid + 1, r);
Merge(l, mid, r);
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
p[i] = i;
}
for(int i = 1; i <= n; i ++) {
scanf("%d", &b[i]);
q[i] = i;
}
sort(p + 1, p + n + 1, cmpa);
sort(q + 1, q + n + 1, cmpb);
for(int i = 1; i <= n; i ++) {
c[p[i]] = q[i];
}
Sort(1, n);
printf("%d", ans);
return 0;
}
数据通过情况
第一种实现方式
第二种实现方式
最后的话
一定一定不要 Copy 代码!!! 并且最好不要边看边写。应该先理解每一步在做什么,对代码有了一个清晰的认识,在自己手打一遍,加深印象。本文中的代码仅供参考,不喜勿喷。
最后的最后,写篇文章不容易,可否来个赞???