Keen On Everything But Triangle HDU - 6601
Problem Description
N sticks are arranged in a row, and their lengths are a1,a2,…,aN.
There are Q querys. For i-th of them, you can only use sticks between li-th to ri-th. Please output the maximum circumference of all the triangles that you can make with these sticks, or print −1 denoting no triangles you can make.
Input
There are multiple test cases.
Each case starts with a line containing two positive integers N,Q(N,Q≤105).
The second line contains N integers, the i-th integer ai(1≤ai≤109) of them showing the length of the i-th stick.
Then follow Q lines. i-th of them contains two integers li,ri(1≤li≤ri≤N), meaning that you can only use sticks between li-th to ri-th.
It is guaranteed that the sum of Ns and the sum of Qs in all test cases are both no larger than 4×105.
Output
For each test case, output Q lines, each containing an integer denoting the maximum circumference.
Sample Input
5 3
2 5 6 5 2
1 3
2 4
2 5
Sample Output
13
16
16
Solution:
首先考虑区间最大的三个数能否形成三角形,如果不能,考虑区间第二大、第三大、第四大的三个数,以此类推, 直到能形成三角形。由三角形最小的两条边大于第三边的性质可知,只需要考虑区间的前 大数即可(最坏情况 下区间前几大数形成了斐波那契数列)。
时间复杂度
O
(
n
l
o
g
n
∗
44
)
O(nlogn*44)
O(nlogn∗44)
主席树本可以用C++写
但是,为了使代码变得更长,决定用java(反面教材)
争取下次上万。。
AC Code:
/*
* Copyright (c) 2019 Ng Kimbing, HNU, All rights reserved. May not be used, modified, or copied without permission.
* @Author: Ng Kimbing, HNU.
* @LastModified:2019-07-26 T 10:55:22.489 +08:00
*/
package ACMProblems.DataStructureProblem.ChairmanTree;
import java.io.*;
import java.util.Arrays;
import java.util.StringTokenizer;
public class KeenOnEverything {
public static PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
private static BufferedReader reader;
private static StringTokenizer tokenizer;
static {
setStream(System.in);
}
public static void setStream(InputStream input) {
reader = new BufferedReader(
new InputStreamReader(input));
tokenizer = new StringTokenizer("");
}
public static String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
String line = reader.readLine();
tokenizer = new StringTokenizer(line);
}
return tokenizer.nextToken();
}
public static String readLine() throws IOException {
return reader.readLine();
}
public static int nextInt() throws IOException {
return Integer.parseInt(next());
}
static class ChairmanTree {
private int nodeCount = 0;
private int[] b;
private int[] root;
private int[] sum; //储存节点的id
private int[] leftChild; //储存节点的id
private int[] rightChild; //储存节点的id
private int eleNum;
private void newArray(int size) {
b = new int[size];
root = new int[size];
sum = new int[size * 20]; //储存节点的id
leftChild = new int[size * 20]; //储存节点的id
rightChild = new int[size * 20]; //储存节点的id
}
public ChairmanTree(int size) {
newArray(size);
}
public ChairmanTree(int[] a, int length) {
newArray(length + 1);
b = new int[length + 1];
System.arraycopy(a, 0, b, 1, length);
buildTreeForArray(a, length);
}
public void clear() {
nodeCount = 0;
b = null;
root = null;
sum = null;
leftChild = null;
rightChild = null;
eleNum = 0;
}
/**
* Remove consecutive values from a sequence
*
* @param a the array
* @param first first index.
* @param last last index.
* @return The index designating the end of the resulting sequence.
*/
public static int unique(int[] a, int first, int last) {
if (first == last)
return last;
int result = first;
while (++first != last) {
if (!(a[result] == a[first])) {
a[++result] = a[first];
}
}
return ++result;
}
public static int lowerBound(int[] array, int start, int end, int key) {
int first = start, middle;
int half, len;
len = end - start;
//binary search
while (len > 0) {
half = len >> 1;
middle = first + half;
if (array[middle] < key) {
len = len - half - 1; //search in the right sub-sequence
first = middle + 1;
} else
len = half; //search in the left sub-sequence, middle included.
}
return first;
}
public void buildTreeForArray(int[] a, int length) {
Arrays.sort(b, 1, length + 1);
eleNum = unique(b, 1, length + 1) - 1;
root[0] = ++nodeCount;
initFirstTreeId(root[0], 1, eleNum);
for (int i = 1; i <= length; ++i) {
int indexOfInsertedNum = lowerBound(b, 1, eleNum + 1, a[i - 1]);
root[i] = getNewTree(root[i - 1], 1, eleNum, indexOfInsertedNum);
}
}
/**
* Initialize the first empty tree. (root[0])
*
* @param ID the id of the root
* @param left left
* @param right right
*/
public void initFirstTreeId(int ID, int left, int right) {
if (left == right)
return;
int mid = (left + right) >> 1;
leftChild[ID] = ++nodeCount;
rightChild[ID] = ++nodeCount;
initFirstTreeId(leftChild[ID], left, mid);
initFirstTreeId(rightChild[ID], mid + 1, right);
}
/**
* insert a the value p and create new nodes.
*
* @param oldNode the ID of the predecessor of the new one.
* @param left first index, inclusive
* @param right last index, exclusive.
* @return the new node (an alternative to oldNode)
*/
public int getNewTree(int oldNode, int left, int right, int insertedNum) {
int newNode = ++nodeCount; //Create a new node.
//update the value of the node. (Due to the insertion of a new number)
sum[newNode] = sum[oldNode] + 1;
//set default child.
leftChild[newNode] = leftChild[oldNode];
rightChild[newNode] = rightChild[oldNode];
//if the node is a leaf.
if (left == right)
return newNode;
int mid = (left + right) >> 1;
//Determine which interval the inserted number is in.
if (insertedNum <= mid)
leftChild[newNode] = getNewTree(leftChild[newNode], left, mid, insertedNum);
else
rightChild[newNode] = getNewTree(rightChild[newNode], mid + 1, right, insertedNum);
return newNode;
}
/**
* find the kth number in an interval.
*
* @param oldTreeNode The previous tree corresponding to the lower bound of the interval
* @param newTreeNode The tree corresponding to the upper bound of the interval
* @param nodeLeft The lower bound of current node.
* @param nodeRight The upper bound of the current node.
* @param k index.
* @return return the index of the answer;
*/
public int query(int oldTreeNode, int newTreeNode, int nodeLeft, int nodeRight, int k) {
if (nodeLeft == nodeRight)
return nodeLeft;
//try the whole interval
int mid = ((nodeLeft + nodeRight) >> 1), x = sum[leftChild[newTreeNode]] - sum[leftChild[oldTreeNode]];
//Narrow the search interval
return x >= k ? query(leftChild[oldTreeNode], leftChild[newTreeNode], nodeLeft, mid, k) :
query(rightChild[oldTreeNode], rightChild[newTreeNode], mid + 1, nodeRight, k - x);//pay attention to the parameter:k-x
}
/**
* left-th to right-th (1 based)
*/
public int find(int left, int right, int k) {
return b[query(root[left - 1], root[right], 1, eleNum, k)];
}
public static void main(String[] args) throws Exception {
int[] a = {5, 4, 3, 2, 1, 6, 7, 8, 9};
ChairmanTree chairmanTree = new ChairmanTree(a, a.length);
while (true) {
int l = nextInt();
int r = nextInt();
int k = nextInt();
System.out.println(chairmanTree.find(l, r, k));
}
}
}
private static boolean isTriangle(int l1, int l2, int l3) {
return l2 + l3 > l1;
}
private static ChairmanTree chairmanTree;
private static int findKBig(int l, int r, int k) {
return chairmanTree.find(l, r, r - l + 2 - k);
}
static int[] a = new int[100005];
public static void main(String[] args) {
while (true) {
try {
int n = nextInt();
int q = nextInt();
for (int i = 0; i < n; ++i)
a[i] = nextInt();
chairmanTree = new ChairmanTree(a, n);
for (int i = 0; i < q; ++i) {
int l = nextInt();
int r = nextInt();
long ans = -1;
for (int j = 1; r - l - j > 0 && j <= n && j <= 44; ++j) {
int l1 = findKBig(l, r, j);
int l2 = findKBig(l, r, j + 1);
int l3 = findKBig(l, r, j + 2);
// System.out.println("l1: " + l1 + " l2: " + l2 + " l3: " + l3);
if (isTriangle(l1, l2, l3)) {
ans = (long) l1 + l2 + l3;
break;
}
}
out.println(ans);
// out.flush();
}
} catch (Throwable e) {
out.flush();
break;
}
}
out.flush();
}
}