poj2566 Bound Found

Description

Signals of most probably extra-terrestrial origin have been received and digitalized by The Aeronautic and Space Administration (that must be going through a defiant phase: "But I want to use feet, not meters!"). Each signal seems to come in two parts: a sequence of n integer values and a non-negative integer t. We'll not go into details, but researchers found out that a signal encodes two integer values. These can be found as the lower and upper bound of a subrange of the sequence whose absolute value of its sum is closest to t. 

You are given the sequence of n integers and the non-negative target t. You are to find a non-empty range of the sequence (i.e. a continuous subsequence) and output its lower index l and its upper index u. The absolute value of the sum of the values of the sequence from the l-th to the u-th element (inclusive) must be at least as close to t as the absolute value of the sum of any other non-empty range.

Input

The input file contains several test cases. Each test case starts with two numbers n and k. Input is terminated by n=k=0. Otherwise, 1<=n<=100000 and there follow n integers with absolute values <=10000 which constitute the sequence. Then follow k queries for this sequence. Each query is a target t with 0<=t<=1000000000.

Output

For each query output 3 numbers on a line: some closest absolute sum and the lower and upper indices of some range where this absolute sum is achieved. Possible indices start with 1 and go up to n.

Sample Input

5 1
-10 -5 0 5 10
3
10 2
-9 8 -7 6 -5 4 -3 2 -1 0
5 11
15 2
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
15 100
0 0

Sample Output

5 4 4
5 2 8
9 1 1
15 1 15
15 1 15

Source

Ulm Local 2001
/*
这是一道尺取法的好题, 是深刻反映尺取思想的. 并且边界条件比较严格, 很考验编码能力.

尺取法是一种技巧, 主要运用在区间范围满足某种性质的问题.
如果一道题是满足尺取法的, 那么它是具有某种单调性的.

比如说 poj3061 Subsequence
这道题是求一段连续的区间和是大于等于S的, 最小的区间长度,
因为所有数字都是正数, 所以当用lo, hi表示一段区间时,
当hi向后移动时, 区间和是上升的,
当lo向后移动时, 区间和是下降的.
也就是说, 当向后移动lo时, 区间和下降, 为了使区间和上升, 只能向后移动hi.
这就是一个单调性.

再比如说 poj3320 Jessica's Reading Problem
这道题是求一个最小的区间长度, 这个区间有Jessica要复习的所有知识.
用lo, hi表示区间, 初始都为0, 区间为空,
首先要不断递增hi, 使[lo, hi)这段区间覆盖了所有的知识点.
接着递增lo, 直到[lo, hi)这段区间首次没有覆盖所有的知识点.
这时候, 只有把hi递增才能使[lo, hi)覆盖所有的知识点.
这样不断的递增lo, hi, 也满足尺取法的单调性.
具体做法可以通过离散化 + 数组模拟

而找到Bound Found和上述两题是不一样的, 这道题初看是找不到单调性的, 必须自己构造.
这道题要求的是一段区间, 其区间和的绝对值是最接近t的.
由于数字是有正有负的,
如果用lo, hi表示一段区间的话, 递增lo, 不一定会使区间和下降,
递增hi, 不一定会使区间和上升. 这不满足尺取法要求的单调性.

如何做呢?
一段区间的和, 我们通常可以用前缀和表示.
即[lo, hi] = prefix(hi) - prefix(lo - 1)
这道题要求的是一段连续的区间, 而任意来两个前缀和相减是可以表示一段连续区间的和的.
这样我们可以通过对前缀和数组排序, 这样这个数组就是单调的了.
用lo, hi表示两个数组索引, 并且 prefix[hi] - prefix[lo]确实是一段区间的和的绝对值
递增hi, 区间和的绝对值上升,
递增lo, 区间和的绝对值下降.
这样就找到了尺取法所要求的单调性了.

关键之处就是对前缀和数组排序.
并且细节要求很苛刻, 需要细细体会
*/
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 1e5 + 600;
const int infinite = 0x3f3f3f3f;
int a[MAXN];
pair prefix[MAXN];
int n, k, t;
int ans, l, r;
void compare(int lo, int hi){
    int sum = lo == hi? -infinite: prefix[hi].first - prefix[lo].first;
    if(abs(sum - t) < abs(ans - t)){
        ans = sum;
        l = lo;
        r = hi;
    }
}
int main(){
    while(scanf("%d%d", &n, &k) && n + k){
        prefix[0] = make_pair(0, 0);// 如果不加0, 是无法用两个索引表示出一段区间的
        for(int i = 0; i < n; ++i){
            scanf("%d", a + i);
            prefix[i + 1] = make_pair(prefix[i].first + a[i], i + 1);// prefix[i]表示这是a数组前i个数字的和
        }

        sort(prefix, prefix + n + 1);

        while(k--){
            scanf("%d", &t);
            int lo = 0, hi = 0;
            ans = -infinite;
            while(true){
                while((hi < n) && (lo == hi? -infinite: prefix[hi].first - prefix[lo].first) < t){
                    ++hi;
                }// 递增hi, 使这段区间和的绝对值首次大于 t

                if((lo == hi? -infinite: prefix[hi].first - prefix[lo].first) < t){
                    compare(lo, hi);
                    break;
                }// 如果区间和的绝对值是小于t的话, 就退出
                compare(lo, hi - 1);// 最优解只能在 [lo, hi - 1]
                compare(lo, hi);// 和 [lo, hi]之间了

                while(t <= (lo == hi? -infinite: prefix[hi].first - prefix[lo].first)){
                    ++lo;
                }// 递增lo, 使区间和的绝对值首次小于 t
                compare(lo - 1, hi);
                compare(lo, hi);
            }
            l = prefix[l].second;
            r = prefix[r].second;
            if(r < l){
                swap(r, l);
            }
            // Output
            printf("%d %d %d\n", ans, l + 1, r);
        }// 上述写法, 时间复杂度的常数较小, 但是细节处在意过多
    }
    return 0;
}

你可能感兴趣的:(ACM,技巧)