0

この CodeChef の問題を解決しようとしています:

テーブルには N 個のコインがあり、0 から N - 1 までの番号が付けられています。
次の 2 種類の操作を実行する必要があります。

  1. A から B までの番号のコインをすべて裏返します。これは、コマンド「0 A B」で表されます。

  2. A から B までの番号のコインのうち、表が何枚あるか答えてください。これは、コマンド「1 A B」で表されます。

入力: 最初の行には 2 つの整数 N と Q が含まれます。次の Q 行はそれぞれ、前述のように "0 A B" または "1 A B" のいずれかの形式です。

出力: 対応するクエリに必要な回答を含む「1 A B」形式のクエリごとに 1 行を出力します。

私が使用したのはセグメントツリーです。そのため、ユーザーがタイプ 1 AB のクエリを入力するたびに、出力はその間隔 [A,B] での合計になります。ただし、Time Limit Exceeded エラーが発生します。エラーは更新ステップ 0 A B が原因だと思います。配列内の要素を更新した後、ツリーを再構築します。コードを以下に示します。誰かがより迅速に更新する方法を教えてくれますか?

ところで-サンプル入力に必要な出力を取得しています。

public class SegmentTree
{
    private int[] tree;
    private int maxsize;
    private int height;
    private static int elems[];
    private  final int STARTINDEX = 0; 
    private  final int ENDINDEX;
    private  final int ROOT = 0;

    public SegmentTree(int size)
    {
        height = (int)(Math.ceil(Math.log(size) /  Math.log(2)));
        maxsize = 2 * (int) Math.pow(2, height) - 1;
        tree = new int[maxsize];
        ENDINDEX = size - 1; 
    }

    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 int 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 0;
        }

        int mid = mid(startIndex, endIndex);

        return  getSumUtil(startIndex, mid, queryStart, queryEnd, leftchild(current)) 
                 + getSumUtil( mid + 1, endIndex, queryStart, queryEnd, rightchild(current));
    }

    public int getSum(int queryStart, int queryEnd)
    {
        if(queryStart < 0 || queryEnd > tree.length)
        {
            return -1;
        }

        return getSumUtil(STARTINDEX, ENDINDEX, queryStart, queryEnd, ROOT);
    }

    private int constructSegmentTreeUtil(int startIndex, int endIndex, int current)
    {
        if (startIndex == endIndex)
        {
            tree[current] = elems[startIndex];
            return tree[current];   
        }

        int mid = mid(startIndex, endIndex);

        tree[current] = constructSegmentTreeUtil(startIndex, mid, leftchild(current))
                           + constructSegmentTreeUtil(mid + 1, endIndex, rightchild(current));

        return tree[current];
    }

    public void constructSegmentTree()
    {
        constructSegmentTreeUtil(STARTINDEX, ENDINDEX, ROOT);   
    }

    public static void main(String[]args) throws IOException
    {
        BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer str = new StringTokenizer(buf.readLine());
        int n = Integer.parseInt(str.nextToken());
        int q = Integer.parseInt(str.nextToken());
        SegmentTree segmentTree = new SegmentTree(n);
        int elements[] = new int[n];
        for(int i = 0; i < n; i++) {
            elements[i] = 0;
        }
        elems = elements;
        segmentTree.constructSegmentTree();
        while (q-- > 0) {
            str = new StringTokenizer(buf.readLine());
            int x = Integer.parseInt(str.nextToken());
            int a = Integer.parseInt(str.nextToken());
            int b = Integer.parseInt(str.nextToken());
            if(x == 0) {
                for(int j = a; j <= b; j++)
                {
                    elems[j] = elems[j]^1;
                }
                segmentTree.constructSegmentTree();
            }
            else {
                int num = segmentTree.getSum(a, b);
                System.out.println(num);
            }
        }
    }   
}

編集:

GeeksForGeeks によると、ツリーの構築コストは O(n) で、更新方法は O(log n) です。したがって、更新のための新しいメソッドは次のとおりです。

private void updateTreeUtil(int startIndex, int endIndex, int updatePos, int update, int current)
{
    if ( updatePos < startIndex || updatePos > endIndex)
    {
        return;
    }

    tree[current] = tree[current] + update;

    if (startIndex != endIndex)
    {
        int mid = mid(startIndex, endIndex);
        updateTreeUtil(startIndex, mid, updatePos, update, leftchild(current));
        updateTreeUtil(mid+1, endIndex, updatePos, update, rightchild(current));
    }
}

public void update(int update, int updatePos)
{
    int updatediff = update - elems[updatePos];
    elems[updatePos] = update;
    updateTreeUtil(STARTINDEX, ENDINDEX, updatePos, updatediff, ROOT);
}

そして、main メソッドの if ループが次のように変更されました。

if(x == 0) {
    for(int j = a; j <= b; j++)
    {
        segmentTree.update(elems[j]^1, j);
    }
}

しかし、それでも TLE エラーが発生します。

4

1 に答える 1

0

GeeksForGeeks のチュートリアルでは、単一の要素を更新する場合、更新の実行時間は O(log n) です。ただし、一定間隔で更新を行う場合は、Lazy Propagation を使用して O(log n) の更新時間を確保する必要があります。これは基本的に、訪問したノードのみを更新するため、訪問したノードの合計が正しいことを確認します。遅延伝播に関する多くの優れたチュートリアルを検索できます。たとえば、次のようになります。

http://se7so.blogspot.hk/2012/12/segment-trees-and-lazy-propagation.html

それが役に立てば幸いです。

于 2015-06-28T06:27:29.247 に答える