poj3067 japan 树状数组

Japan
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 14431   Accepted: 3868

Description

Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coast and M cities on the West coast (M <= 1000, N <= 1000). K superhighways will be build. Cities on each coast are numbered 1, 2, ... from North to South. Each superhighway is straight line and connects city on the East coast with city of the West coast. The funding for the construction is guaranteed by ACM. A major portion of the sum is determined by the number of crossings between superhighways. At most two superhighways cross at one location. Write a program that calculates the number of the crossings between superhighways.

Input

The input file starts with T - the number of test cases. Each test case starts with three numbers – N, M, K. Each of the next K lines contains two numbers – the numbers of cities connected by the superhighway. The first one is the number of the city on the East coast and second one is the number of the city of the West coast.

Output

For each test case write one line on the standard output:
Test case (case number): (number of crossings)

Sample Input

1
3 4 4
1 4
2 3
3 2
3 1

Sample Output

Test case 1: 5

 

1. 逆序数

 

所谓逆序数,就是指一个序列S[i],统计处于序列的每个数的比这个数大并且排在它前面的数的数目,然后对于所有数,把这个数目加起来求和就是了。

比如4 3 1 2

4第一个,所以数目为0

3的前面是4,大于3的数目为1

1的前面是4 3 ,大于1的数目为2

2的前面是4 3 1,大于2的数目为2

所以逆序数为1+2+2 = 5

 

求逆序数的两种方法

常规方法是按照逆序数的规则做,结果复杂度是O(n*n),一般来说,有两种快速的求逆序数的方法

分别是归并排序和树状数组法

 

 

2. 归并排序

归并排序是源于分而治之思想,详细的过程可以查阅其他资料,总体思想是划分一半,各自排好序后将两个有序序列合并起来。

 

如何修改归并排序求逆序数?

首先我们假设两个有序序列a[i]和b[i],当合并时:

由于a[i]已是有序,所以对于a[i]的各个元素来说,排在它前面且比它大的数目都是0

当b[i]中含有比a[i]小的元素时,我们必然将b[i]元素插到前面,那么就是说,在b[i]原先位置到该插的位置中,所有数都比b[i]大且排在它前面

所以这是b[i]的数目为新插入位置newPos - 原来位置oldPos

 

那么对于一半的序列又怎么做呢?我们知道,归并排序会继续向下递归,而递归完成返回后将是两组有序的序列,并且拿到局部的逆序数,

所以在Merge函数中添加这一计数操作即可

 

 

 

 

代码示例如下:int L[M];

int R[M];

 

const int Max = 1 <<30;

__int64 change = 0;

 

void Merge(int *data,int left,int divide,int right)

{

int lengthL = divide - left;

int lengthR = right - divide;

 

for(int i = 0; i < lengthL; ++i)

{

L[i] = data[left + i];

}

for(int i = 0; i < lengthR; ++i)

{

R[i] = data[divide + i];

}

L[lengthL] = R[lengthR] = Max;

int i = 0;

int j = 0;

for(int k = left; k < right; ++k)

{

if(L[i] <= R[j])

{

data[k] = L[i];

++i;

}

else

{

change += divide - i - left ;

data[k] = R[j];

++j;

}

}

 

}

 

void MergeSort(int *data,int left,int right)

{

if(left < right -1)

{

int divide = (left + right)/2;

MergeSort(data,left,divide);

MergeSort(data,divide,right);

Merge(data,left,divide,right);

}

}

 

 

3. 树状数组

求逆序数的另外一种方法是使用树状数组

对于小数据,可以直接插入树状数组,对于大数据,则需要离散化,所谓离散化,就是将

100 200 300 400 500 ---> 1 2 3 4 5

 

这里主要利用树状数组解决计数问题。

 

首先按顺序把序列a[i]每个数插入到树状数组中,插入的内容是1,表示放了一个数到树状数组中。

然后使用sum操作获取当前比a[i]小的数,那么当前i - sum则表示当前比a[i]大的数,如此反复直到所有数都统计完,

比如

4 3 1 2

i = 1 : 插入4 : update(4,1),sum(4)返回1,那么当前比4大的为i - 1 = 0;

i = 2 : 插入3 : update(3,1),sum(3)返回1,那么当前比3大的为i - 1 = 1;

i = 3 : 插入1 : update(1,1),sum(1)返回1,那么当前比1大的为i - 1 = 2;

i = 4 : 插入2 : update(2,1),sum(2)返回2,那么当前比2大的为i - 2 = 2;

 

过程很明了,所以逆序数为1+2+2=5

 

代码示例如下:

 

//树状数组

__int64 sums[1005];

int len;

 

inline int lowbit(int t)

{

return t & (t^(t-1));

}

 

void update(int _x,int _value)

{

while(_x <= len)

{

sums[_x] += _value;

_x += lowbit(_x);

}

}

 

__int64 sum(int _end)//get sum[1_end]

{

__int64 ret = 0;

while(_end > 0)

{

ret += sums[_end];

_end -= lowbit(_end);

}

return ret;

}

 

//求逆序数

 

__int64 ret = 0;

for (__int64 i = 0; i < k; ++i)

{

update(a[i],1);

ret += (i+1) - sum(a[i]);

}

 

View Code
package TreeArray;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Japan__poj__3067 {
    static int[] c;
    static Data[] a;
    static int n, m, k;
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(
            new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

    public static void main(String[] args) throws IOException {
        int T = nextInt();
        for (int cae = 1; cae <= T; cae++) {
            n = nextInt();
            m = nextInt();
            k = nextInt();
            long sum = 0;
            c = new int[m + 1];
            a = new Data[k + 1];
            init(k);
            Arrays.sort(a);

            for (int i = 1; i <= k; i++) {
                modify(a[i].west, 1);
                sum += (i - getSum(a[i].west));

            }
            out.println("Test case " + cae + ": " + sum + "");

        }
        out.flush();

    }

    static void init(int k) throws IOException {

        a[0] = new Data(0, 0);
        for (int i = 1; i <= k; i++) {
            a[i] = new Data(nextInt(), nextInt());
        }
    }

    static int lowbit(int x) {
        return x & -x;
    }

    static int getSum(int x) {
        int sum = 0;
        for (int i = x; i > 0; i -= lowbit(i))
            sum += c[i];
        return sum;
    }

    static void modify(int x, int delta) {

        for (int i = x; i <=m; i += lowbit(i))
            c[i] += delta;
    }

    static int nextInt() throws IOException {
        in.nextToken();
        return (int) in.nval;
    }

    static String next() throws IOException {
        in.nextToken();
        return in.sval;
    }

}

class Data implements Comparable<Data> {
    int east, west;

    public Data(int east, int west) {
        this.east = east;
        this.west = west;
    }

    @Override
    public int compareTo(Data o) {
        if (this.east == o.east) {
            if (this.west > o.west)
                return 1;
            else
                return -1;
        } else {
            if (this.east > o.east)
                return 1;
            else
                return -1;
        }

    }

}

 

posted on 2012-07-21 19:20  wpdxzabm  阅读(106)  评论(0)    收藏  举报

导航