0

I'm solving this problem by using a segment tree. I am saving the sum, max, leftmost max, and the right most max at every node. I then search the graph to find the answer to a specific interval. How could I increase the speed of this code?

import java.util.Scanner;
//TLE
class GSS1 {


static class Node{
    int max;
    int MaxL;
    int MaxR;
    int sum;

    public Node(int max, int MaxL, int MaxR, int sum){
        this.max=max;
        this.MaxL=MaxL;
        this.MaxR=MaxR;
        this.sum=sum;
    }

    public Node(){

    }
}

static class SegmentTree{

    private Node[] tree;
    private int maxsize;
    private int height;

    private  final int STARTINDEX = 0; 
    private  final int ENDINDEX;
    private  final int ROOT = 0;
    Node s;

    public SegmentTree(int size){
        height = (int)(Math.ceil(Math.log(size) /  Math.log(2)));
        maxsize = 2 * (int) Math.pow(2, height) - 1;
        tree = new Node[maxsize];
        for(int i=0;i<tree.length;i++){
            tree[i]=new Node();
        }
        ENDINDEX = size - 1; 
        s=new Node();
        s.MaxL=Integer.MIN_VALUE;
        s.MaxR=Integer.MIN_VALUE;
        s.sum=Integer.MIN_VALUE;
        s.max=Integer.MIN_VALUE;

    }




    private int leftchild(int pos){
        return 2 * pos + 1;
    }

    private int rightchild(int pos){
        return 2 * pos + 2;
    }

    private int mid(int start, int end){
        return (start + (end - start) / 2); 
    }

    private Node constructSegmentTreeUtil(int[] elements, int startIndex, int endIndex, int current){
        if (startIndex == endIndex)
        {
            tree[current].max=tree[current].MaxL=tree[current].MaxR=tree[current].sum=elements[startIndex]; 
            return tree[current];
        }
        int mid = mid(startIndex, endIndex);
        Node left=constructSegmentTreeUtil(elements, startIndex, mid, leftchild(current));
        Node right=constructSegmentTreeUtil(elements, mid + 1, endIndex, rightchild(current));
        tree[current].max = Math.max(left.max, right.max);
        tree[current].MaxL = Math.max(left.MaxL , left.sum+right.MaxL);
        tree[current].MaxR = Math.max(right.MaxR , right.sum+left.MaxR);
        tree[current].sum = left.sum+right.sum;
        return tree[current];
    }

    public void constructSegmentTree(int[] elements){
        constructSegmentTreeUtil(elements, STARTINDEX, ENDINDEX, ROOT); 
    }

    private Node getSumUtil(int startIndex, int endIndex, int queryStart, int queryEnd, int current){

        if (queryStart <= startIndex && queryEnd >= endIndex ){
            return tree[current];
        }
        if (endIndex < queryStart || startIndex > queryEnd){
            return s;
        }
        int mid = mid(startIndex, endIndex);

        Node left=getSumUtil(startIndex, mid, queryStart, queryEnd, leftchild(current));
        Node right=getSumUtil( mid + 1, endIndex, queryStart, queryEnd, rightchild(current));

        Node current_Node=new Node();
        current_Node.max = Math.max(left.max, right.max);
        current_Node.MaxL = Math.max(left.MaxL , left.sum+right.MaxL);
        current_Node.MaxR = Math.max(right.MaxR , right.sum+left.MaxR);
        current_Node.sum = left.sum+right.sum;
        return current_Node;


    }

    public int getMaxSum(int queryStart, int queryEnd){
        if(queryStart < 0 || queryEnd > tree.length)
        {System.out.println("inside negative");
            return Integer.MIN_VALUE;
        }
        return getMax(getSumUtil(STARTINDEX, ENDINDEX, queryStart, queryEnd, ROOT));
    }

    public int getMax(Node r){
        return Math.max(Math.max(r.max, r.MaxL),Math.max(r.MaxR, r.sum));
    }

    public int getFirst(){
        return tree[0].MaxL;
    }

}


public static void main(String[] args) {
    Scanner input=new Scanner(System.in);

    int numbers[]=new int [input.nextInt()];

    for(int i=0;i<numbers.length;i++){
        numbers[i]=input.nextInt();
    }

    SegmentTree tree=new SegmentTree(numbers.length);
    tree.constructSegmentTree(numbers);

    int cases=input.nextInt();

    int x;
    int y;
    int query;
    for(int i=0;i<cases;i++){
        x=input.nextInt()-1;
        y=input.nextInt()-1;

        System.out.println(tree.getMaxSum(x, y));
    }






    }

}
Sᴀᴍ Onᴇᴌᴀ
  • 8,218
  • 8
  • 36
  • 58
The Bear
  • 199
  • 2
  • 8
  • What's the complexity of your algorithm? Have you tried running it on random input and checking how long it takes? From my experience your should get at least a runtime of one fourth the time limit on a modern machine for it to pass on Pyramid – Niklas B. Apr 30 '14 at 02:07
  • It passes on the first 8 test cases, but fails on the ninth so I'm assuming that the 9th test cases has the largest input. I think that the complexity of my algorithm is O(nlogn) – The Bear Apr 30 '14 at 03:05
  • How fast is it on your machine for an array of size 50000 with random values and the maximum number of random queries – Niklas B. Apr 30 '14 at 03:07
  • I haven't tested that. – The Bear Apr 30 '14 at 03:07
  • Will do so in the future and will update you – The Bear Apr 30 '14 at 03:08
  • Is your algorithm correct? I see something wrong in the part `current_Node.MaxL = Math.max(left.MaxL , left.sum+right.MaxL);`, you are assuming that max left is sum of the left plus the maximum left side of the right? How about the middle part? `left.MaxR + right.MaxL`! I think there are some cases you did not handled yet in this problem, which segment tree may not be a good choice – Pham Trung Apr 30 '14 at 04:56
  • @PhamTrung That's the maximum sum of a subarray that goes through the left border of the segment, so the recurrence is correct. Segment tree can definitely be used to solve this in O(n log n), [even with range updates](http://www.spoj.com/problems/GSS3/) – Niklas B. Apr 30 '14 at 06:40
  • 1
    @TheBear: I think it's no coincidence that only few people managed to solve this using Java. The time limit is pretty tough, so you will probably need a lot of constant optimizations to squeeze it through if you want to stick with Java. Maybe using an array to represent the tree (like an implicit heap) is faster than allocating each node at once. I/O speed is probably also an issue here, `Scanner` is notoriously slow, which is why people typically use they're own buffered I/O integer parsing routines for competitive programming purposes. – Niklas B. Apr 30 '14 at 06:44
  • @NiklasB. yes, using segment tree is fine, but I think he missed a case when the sum is at the middle and doesn't start from left or right end of a segment. I think he missed this `left.maxR + right.maxL` – Pham Trung Apr 30 '14 at 07:19
  • 1
    @PhamTrung Yes you're right, it should be `tree[current].max = Math.max(left.max, Math.max(right.max, left.maxR + right.maxL));` – Niklas B. Apr 30 '14 at 07:33
  • I don't see how this is a missed case...can you give me a test case where my code fails? – The Bear Apr 30 '14 at 14:36
  • This should fail on a lot of cases, just try generating random test cases and testing it against a brute-force solution – Niklas B. Apr 30 '14 at 15:51
  • Can you explain in greater detail what this "missed" case is because the one's I'm running right now give me the correct answer – The Bear Apr 30 '14 at 16:43
  • Ok, try this -1 2 3 -1 and the range is 1 to 4 – Pham Trung Apr 30 '14 at 17:00
  • ok I now see the problem – The Bear Apr 30 '14 at 17:04
  • 1
    I can not imagine that you would not have found this with random testing. *Always* use random testing on SPOJ or similar sites when you are out of ideas on what cases your code would not work – Niklas B. Apr 30 '14 at 18:17

1 Answers1

1

Your approach is right but I/O speed also matters for this question, as the timing constraints are very tight. You should use a custom reader since Scanner is very slow. Use the below mentioned class for reading input.

class Reader {
    final private int BUFFER_SIZE = 1 << 16;private DataInputStream din;private byte[] buffer;private int bufferPointer, bytesRead;
    public Reader(){din=new DataInputStream(System.in);buffer=new byte[BUFFER_SIZE];bufferPointer=bytesRead=0;
    }public Reader(String file_name) throws IOException{din=new DataInputStream(new FileInputStream(file_name));buffer=new byte[BUFFER_SIZE];bufferPointer=bytesRead=0;
    }public String readLine() throws IOException{byte[] buf=new byte[64];int cnt=0,c;while((c=read())!=-1){if(c=='\n')break;buf[cnt++]=(byte)c;}return new String(buf,0,cnt);
    }public int nextInt() throws IOException{int ret=0;byte c=read();while(c<=' ')c=read();boolean neg=(c=='-');if(neg)c=read();do{ret=ret*10+c-'0';}while((c=read())>='0'&&c<='9');if(neg)return -ret;return ret;
    }public long nextLong() throws IOException{long ret=0;byte c=read();while(c<=' ')c=read();boolean neg=(c=='-');if(neg)c=read();do{ret=ret*10+c-'0';}while((c=read())>='0'&&c<='9');if(neg)return -ret;return ret;
    }public double nextDouble() throws IOException{double ret=0,div=1;byte c=read();while(c<=' ')c=read();boolean neg=(c=='-');if(neg)c = read();do {ret=ret*10+c-'0';}while((c=read())>='0'&&c<='9');if(c=='.')while((c=read())>='0'&&c<='9')ret+=(c-'0')/(div*=10);if(neg)return -ret;return ret;
    }private void fillBuffer() throws IOException{bytesRead=din.read(buffer,bufferPointer=0,BUFFER_SIZE);if(bytesRead==-1)buffer[0]=-1;
    }private byte read() throws IOException{if(bufferPointer==bytesRead)fillBuffer();return buffer[bufferPointer++];
    }public void close() throws IOException{if(din==null) return;din.close();}
}
zipp
  • 1,116
  • 13
  • 27
Manu
  • 317
  • 4
  • 16