ALGO-7 逆序对
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
Alice是一个让人非常愉跃的人!他总是去学习一些他不懂的问题,然后再想出许多稀奇古怪的题目。这几天,Alice又沉浸在逆序对的快乐当中,他已近学会了如何求逆序对对数,动态维护逆序对对数等等题目,他认为把这些题让你做简直是太没追求了,于是,经过一天的思考和完善,Alice终于拿出了一道他认为差不多的题目:
有一颗2n-1个节点的二叉树,它有恰好n个叶子节点,每个节点上写了一个整数。如果将这棵树的所有叶子节点上的数从左到右写下来,便得到一个序列a[1]…a[n]。现在想让这个序列中的逆序对数量最少,但唯一的操作就是选树上一个非叶子节点,将它的左右两颗子树交换。他可以做任意多次这个操作。求在最优方案下,该序列的逆序对数最少有多少。
Alice自己已近想出了题目的正解,他打算拿来和你分享,他要求你在最短的时间内完成。
输入格式
第一行一个整数n。
下面每行,一个数x。
如果x=0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,如果x≠0,表示这个节点是叶子节点,权值为x。
输出格式
输出一个整数,表示最少有多少逆序对。
测试样例
输入:
3
0
0
3
1
2
输出:
1
数据规模与约定
对于20%的数据,n <= 5000。
对于100%的数据,1 <= n <= 200000,0 <= a[i]<2^31。
鏖战几天终于用JavaAC了
AC code:
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.StringTokenizer;
import java.io.*;
public class Main {
static Node[] tree;
static int inReturn;
static int[] noLeaf;
static Queue<PQItem> inQ;
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
int n = in.nextInt(); long cnt = 0;
noLeaf = new int[n];
tree = new Node[n*2];
tree[0] = new Node(0);
tree[1] = new Node(1);
build(tree[1], 1, in);
inQ = new PriorityQueue();
for (int i = n - 2; i >= 0; i--) cnt += merge(noLeaf[i]);
System.out.print(cnt);
}
static void build(Node node, int offset, InputReader in) throws IOException {
LinkedList<Node> queue = new LinkedList();
int index = 0;
queue.addFirst(node);
while (queue.size() > 0) {
Node now = queue.poll();
noLeaf[index] = now.weight;
now.weight = in.nextInt();
if (now.weight == 0) {
now.left = ++offset;
queue.addFirst(tree[++offset] = new Node(offset));
now.right = offset;
queue.addFirst(tree[offset - 1] = new Node(offset - 1));
index++;
} else now.size = 1;
}
}
static long merge(int node) {
long LS = tree[tree[node].left].size, RS = tree[tree[node].right].size, cnt = 0;
if (LS > RS) {
Queue(tree[node].right);
tree[node] = tree[tree[node].left];
} else {
Queue(tree[node].left);
tree[node] = tree[tree[node].right];
}
while (inQ.size() > 0) {
inReturn = 0;
insert(node, reSet(inQ.poll().node));
cnt += inReturn;
}
return Math.min(cnt, LS * RS - cnt);
}
static void Queue(int node) {
if (tree[node].left != 0) Queue(tree[node].left);
if (tree[node].right != 0) Queue(tree[node].right);
if (tree[node].weight != 0) inQ.offer(new PQItem(tree[node].weight, node));
}
static void insert(int node, int value) {
++tree[node].size;
if (tree[node].weight >= tree[value].weight) {
if (tree[node].left != 0) insert(tree[node].left, value);
else tree[node].left = value;
maintain(node, true);
} else {
inReturn += tree[tree[node].left].size + 1;
if (tree[node].right != 0) insert(tree[node].right, value);
else tree[node].right = value;
maintain(node, false);
}
}
static int reSet(int node) {
tree[node].size = 1;
tree[node].left = 0;
tree[node].right = 0;
return node;
}
static void LeftRotate(int node) {
Node temp = tree[node];
int r = temp.right;
tree[r].size = temp.size;
temp.right = tree[r].left;
temp.size = tree[temp.left].size + tree[temp.right].size + 1;
tree[r].left = r;
tree[node] = tree[r];
tree[r] = temp;
}
static void RightRotate(int node) {
Node temp = tree[node];
int l = temp.left;
tree[l].size = temp.size;
temp.left = tree[l].right;
temp.size = tree[temp.left].size + tree[temp.right].size + 1;
tree[l].right = l;
tree[node] = tree[l];
tree[l] = temp;
}
static void maintain(int node, boolean flag) {
if (flag) {
if (tree[tree[tree[node].left].left].size > tree[tree[node].right].size) RightRotate(node);
else if (tree[tree[tree[node].left].right].size > tree[tree[node].right].size) {
LeftRotate(tree[node].left);
RightRotate(node);
} else return;
} else {
if (tree[tree[tree[node].right].right].size > tree[tree[node].left].size) LeftRotate(node);
else if (tree[tree[tree[node].right].left].size > tree[tree[node].left].size) {
RightRotate(tree[node].right);
LeftRotate(node);
} else return;
}
maintain(tree[node].left, true);
maintain(tree[node].right, false);
}
static class Node {
int weight;
int size;
int left;
int right;
Node(int weight) { this.weight = weight; }
}
static class PQItem implements Comparable<PQItem> {
int index;
int node;
PQItem (int index, int node) {
this.node = node;
this.index = index;
}
@Override
public int compareTo(PQItem o) {
if (this.index > o.index) return -1;
return +1;
}
}
static class InputReader {
BufferedReader read;
StringTokenizer tokens;
String delimiters;
InputReader (InputStream in, String delimiters) {
this.read = new BufferedReader(new InputStreamReader(in));
this.delimiters = delimiters;
this.tokens = new StringTokenizer("", delimiters);
}
InputReader (InputStream in) { this(in, " \t\n\r\f"); }
String next() throws IOException {
while (!tokens.hasMoreTokens()) tokens = new StringTokenizer(read.readLine(), delimiters);
return tokens.nextToken();
}
int nextInt() throws IOException { return Integer.parseInt(next()); }
void close() throws IOException { read.close(); }
}
}
第一版(MLE):
import java.io.*;
import java.util.*;
public class Main {
static List<Integer>[] Llist, Rlist;
static int tree[], nodeIndex, listIndex;
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
int n = in.nextInt() - 1, node = n * 2 + 1, cnt = 0;
tree = new int[node];
Llist = new List[n];
Rlist = new List[n];
for (int i = 0; i < node; i++) tree[i] = in.nextInt();
if (tree[0] == 0) {
Llist[0] = getList();
Rlist[0] = getList();
} else {
nodeIndex = listIndex = -1;
getList();
}
for (int l = 0, LWeight = 0, RWeight = 0; l < n; l++, LWeight = 0, RWeight = 0) {
for (int i: Llist[l])
for (int j: Rlist[l])
if (i > j) LWeight++;
else RWeight++;
cnt += Math.min(LWeight, RWeight);
}
System.out.print(cnt);
}
static List getList() {
if (tree[++nodeIndex] == 0) {
final int index = ++listIndex;
Llist[index] = getList();
Rlist[index] = getList();
return new ArrayList(Llist[index]) {{ addAll(Rlist[index]); }};
}
return new ArrayList() {{ add(tree[nodeIndex]); }};
}
static class InputReader {
BufferedReader read;
StringTokenizer tokens;
String delimiters;
InputReader (InputStream in, String delimiters) {
this.read = new BufferedReader(new InputStreamReader(in));
this.delimiters = delimiters;
this.tokens = new StringTokenizer("", delimiters);
}
InputReader (InputStream in) { this(in, " \t\n\r\f"); }
String next() throws IOException {
while (!tokens.hasMoreTokens()) tokens = new StringTokenizer(read.readLine(), delimiters);
return tokens.nextToken();
}
int nextInt() throws IOException { return Integer.parseInt(next()); }
void close() throws IOException { read.close(); }
}
}
遭受测试用例的毒打,如果是比赛的话我可能就信心满满的提交上去了
稍作优化调整(MLE):
import java.io.*;
import java.util.*;
public class Main {
static Node[] noLeafPQ;
static List<Integer>[] listPool;
static int tree[], nodeIndex, PQIndex;
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
int n = in.nextInt() - 1, node = n * 2 + 1, cnt = 0;
tree = new int[node]; nodeIndex = PQIndex = -1;
noLeafPQ = new Node[n];
listPool = new List[n];
for (int i = 0; i < node; i++) tree[i] = in.nextInt();
put();
for (int i = n - 1, poolIndex = 0; i >= 0; i--, poolIndex++) {
List Llist = new ArrayList();
List Rlist = new ArrayList();
getList(noLeafPQ[i].left, Llist);
getList(noLeafPQ[i].right, Rlist);
noLeafPQ[i].weight = -262144 | poolIndex;
cnt += getCount(Llist, Rlist);
Llist.addAll(Rlist);
listPool[poolIndex] = Llist;
}
System.out.print(cnt);
}
static int getCount(List<Integer> L, List<Integer> R) {
int l = 0, r = 0;
for (int i: L)
for (int j: R)
if (i > j) l++;
else r++;
return Math.min(l, r);
}
static void getList(Node N, List list) {
if (N.weight == 0) {
getList(N.left, list);
getList(N.right, list);
} else if (N.weight > 0) list.add(N.weight);
else list.addAll(listPool[N.weight & 262143]);
}
static Node put() {
if (tree[++nodeIndex] == 0) {
Node n = new Node(0);
n.left = put();
n.right = put();
return noLeafPQ[++PQIndex] = n;
}
return new Node(tree[nodeIndex]);
}
static class Node {
int weight;
Node left;
Node right;
Node (int weight) { this.weight = weight; }
}
static class InputReader { . . . }
}
很大程度上降低了递归深度,可以看到性能有空前的提升,但还是只有24分
自闭了,兄弟
我再研究会
用C++复写了一遍(MLE):
#include <iostream>
#include <vector>
using namespace std;
struct node {
int weight, left, right;
} *tree;
void build(int &cur);
void getList(int cur, vector<int> &list);
long long clac(vector<int> &L, vector<int> &R);
vector<int> *listPool;
int n, root, *noLeaf;
long long cnt;
int main() {
scanf("%d", &n); --n;
listPool = new vector<int>[n];
tree = new node[n * 2 + 1];
noLeaf = new int[n];
build(root);
for (int i = n - 1, poolIndex = 0; i >= 0; --i, poolIndex++) {
vector<int> Llist;
vector<int> Rlist;
getList(tree[noLeaf[i]].left, Llist);
getList(tree[noLeaf[i]].right, Rlist);
tree[noLeaf[i]].weight = -262144 | poolIndex;
cnt += clac(Llist, Rlist);
Llist.insert(Llist.end(), Rlist.begin(), Rlist.end());
listPool[poolIndex] = Llist;
}
cout << cnt;
}
int treeIndex, noLeafIndex;
void build(int &cur) {
cur = treeIndex++;
scanf("%d", &tree[cur].weight);
if (tree[cur].weight == 0) {
noLeaf[noLeafIndex++] = cur;
build(tree[cur].left);
build(tree[cur].right);
}
}
void getList(int cur, vector<int> &list) {
if (tree[cur].weight == 0) {
getList(tree[cur].left, list);
getList(tree[cur].right, list);
} else if (tree[cur].weight > 0) list.push_back(tree[cur].weight);
else list.insert(list.end(), listPool[tree[cur].weight & 262143].begin(), listPool[tree[cur].weight & 262143].end());
}
long long clac(vector<int> &L, vector<int> &R) {
long long l = 0, r = 0;
for (int i = 0; i < L.size(); ++i)
for (int j = 0; j < R.size(); ++j)
if (L[i] > R[j]) l++;
else r++;
return l < r? l: r;
}
由此可见,是我想问题太片面了
经过一系列艰苦卓绝的优化,主要是考虑继不继续做这道题了,原理都得都懂,但测试平台的Java栈容不足以递归生存树,总之先写个半成品看看有没有跑满分的潜力。
半成品:
import java.io.*;
import java.util.*;
public class Main {
static Node[] tree;
static int[] noLeaf;
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
int n = in.nextInt(); long cnt = 0;
(tree = new Node[n * 2])[1] = new Node(0, 1);
tree[0] = new Node(0, 0);
noLeaf = new int[n];
build(tree[1], 1, in);
for (int i = n - 2; i >= 0; i--) cnt += merge(noLeaf[i]);
System.out.print(cnt);
}
static void build(Node node, int offset, InputReader in) throws IOException {
LinkedList<Node> queue = new LinkedList();
int index = 0;
queue.addFirst(node);
while (queue.size() > 0) {
Node now = queue.poll();
noLeaf[index] = now.weight;
now.weight = in.nextInt();
if (now.weight == 0) {
now.left = ++offset;
queue.addFirst(tree[++offset] = new Node(noLeaf[index], offset));
now.right = offset;
queue.addFirst(tree[offset - 1] = new Node(noLeaf[index], offset - 1));
index++;
} else now.size = 1;
}
}
static long merge(int node) {
long LS = tree[tree[node].left].size, RS = tree[tree[node].right].size, cnt = 0;
if (LS > RS) {
Queue(tree[node].right);
Overlay(node, tree[node].left);
} else {
Queue(tree[node].left);
Overlay(node, tree[node].right);
}
while (inQ.size() > 0) {
inReturn = 0;
insert(node, reSet(inQ.poll().node));
cnt += inReturn;
}
return Math.min(cnt, LS * RS - cnt);
}
static Queue<PQItem> inQ = new PriorityQueue();
static void Queue(int node) {
if (tree[node].left != 0) Queue(tree[node].left);
if (tree[node].right != 0) Queue(tree[node].right);
if (tree[node].weight != 0) inQ.offer(new PQItem(tree[node].weight, node));
}
static int inReturn;
static void insert(int node, int value) {
++tree[node].size;
if (tree[node].weight >= tree[value].weight) {
if (tree[node].left != 0) insert(tree[node].left, value);
else tree[node].left = value;
} else {
inReturn += tree[tree[node].left].size + 1;
if (tree[node].right != 0) insert(tree[node].right, value);
else tree[node].right = value;
}
}
static void Overlay(int top, int node) { (tree[top] = tree[node]).top = tree[top].top; }
static int reSet(int node) { . . .}
static class Node {
int weight;
int top;
int size;
int left;
int right;
Node(int top, int weight) { this.top = top; this.weight = weight; }
}
static class PQItem implements Comparable<PQItem>{
int index;
int node;
PQItem (int index, int node) {
this.node = node;
this.index = index;
}
@Override
public int compareTo(PQItem o) {
if (this.index > o.index) return -1;
return +1;
}
}
static class InputReader { . . . }
}
至少避免了的报错,接下来实现平衡二叉树就彻底起飞,大概
九成品:
import java.io.*;
import java.util.*;
public class Main {
static Node[] tree;
static int[] noLeaf;
public static void main(String[] args) throws IOException {
InputReader in = new InputReader(System.in);
int n = in.nextInt(); long cnt = 0;
noLeaf = new int[n];
tree = new Node[n * 2];
tree[0] = new Node(0);
tree[1] = new Node(1);
build(tree[1], 1, in);
for (int i = n - 2; i >= 0; i--) cnt += merge(noLeaf[i]);
System.out.print(cnt);
}
static void build(Node node, int offset, InputReader in) throws IOException {
LinkedList<Node> queue = new LinkedList();
int index = 0;
queue.addFirst(node);
while (queue.size() > 0) {
Node now = queue.poll();
noLeaf[index] = now.weight;
now.weight = in.nextInt();
if (now.weight == 0) {
now.left = ++offset;
queue.addFirst(tree[++offset] = new Node(offset));
now.right = offset;
queue.addFirst(tree[offset - 1] = new Node(offset - 1));
index++;
} else now.size = 1;
}
}
static long merge(int node) {
long LS = tree[tree[node].left].size, RS = tree[tree[node].right].size, cnt = 0;
if (LS > RS) {
Queue(tree[node].right);
tree[node] = tree[tree[node].left];
} else {
Queue(tree[node].left);
tree[node] = tree[tree[node].right];
}
while (inQ.size() > 0) {
inReturn = 0;
insert(node, reSet(inQ.poll().node));
cnt += inReturn;
}
return Math.min(cnt, LS * RS - cnt);
}
static Queue<PQItem> inQ = new PriorityQueue();
static void Queue(int node) {
if (tree[node].left != 0) Queue(tree[node].left);
if (tree[node].right != 0) Queue(tree[node].right);
if (tree[node].weight != 0) inQ.offer(new PQItem(tree[node].weight, node));
}
static int inReturn;
static void insert(int node, int value) {
++tree[node].size;
if (tree[node].weight >= tree[value].weight) {
if (tree[node].left != 0) insert(tree[node].left, value);
else {
tree[node].left = value;
maintain(node, true);
}
} else {
inReturn += tree[tree[node].left].size + 1;
if (tree[node].right != 0) insert(tree[node].right, value);
else {
tree[node].right = value;
maintain(node, false);
}
}
}
static int reSet(int node) { . . . }
static void LeftRotate(int node) {
Node temp = tree[node];
int r = temp.right;
tree[r].size = temp.size;
temp.right = tree[r].left;
temp.size = tree[temp.left].size + tree[temp.right].size + 1;
tree[r].left = r;
tree[node] = tree[r];
tree[r] = temp;
}
static void RightRotate(int node) {
Node temp = tree[node];
int l = temp.left;
tree[l].size = temp.size;
temp.left = tree[l].right;
temp.size = tree[temp.left].size + tree[temp.right].size + 1;
tree[l].right = l;
tree[node] = tree[l];
tree[l] = temp;
}
static void maintain(int node, boolean flag) {
if (flag) {
if (tree[tree[tree[node].left].left].size > tree[tree[node].right].size) RightRotate(node);
else if (tree[tree[tree[node].left].right].size > tree[tree[node].right].size) {
LeftRotate(tree[node].left);
RightRotate(node);
} else return;
} else {
if (tree[tree[tree[node].right].right].size > tree[tree[node].left].size) LeftRotate(node);
else if (tree[tree[tree[node].right].left].size > tree[tree[node].left].size) {
RightRotate(tree[node].right);
LeftRotate(node);
} else return;
}
maintain(tree[node].left, true);
maintain(tree[node].right, false);
maintain(node, true);
maintain(node, false);
}
static class Node { . . . }
static class PQItem implements Comparable<PQItem> { . . . }
static class InputReader { . . . }
}
但是性能并没有提升,不过在自己电脑跑测试数据的时候出现了栈溢出错误,如果完整的实现了平衡二叉树,那么 100000 个节点的深度肯定不足以让我爆栈,有时间再写