2019 Multi-University Training Contest 2 Keen On Everything But Triangle【偏爱长代码2.0】

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(nlogn44)

主席树本可以用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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值