2013 ACM/ICPC Asia Regional Changsha Online - J Candies

题意:

给你一排数,有的数告诉你明确的值,有的数不告诉(用-1来表示),然后给出每个数相邻三个数的和,首尾则是相邻两个数的和,有m次询问,问a[i]最大值是多少。 (比赛的时候太挫了,wa了,后来把某些地方改成简单点形式就A了。)


解题思路:

首先可以得到一列等式:

a1 + a2 = sum1;

a1 + a2 + a3 = sum2;

a2 + a3 + a4 = sum3;

a3 + a4 + a5 = sum4;

a4 + a5 + a6 = sum5;

a5 + a6 + a7 = sum6;

...

由等式容易得知a[i] (i % 3 == 0) 的值都可以明确了,现在把这些等式化简一下得:

a1 + a2 = s1;

a2 + a4 = s2;

a4 + a5 = s3;

a5 + a7 = s4;

a7 + a8 = s5;

...

容易发现,如果我们知道了这些等式中的任何一个未知数的值,其他所有的值都可以确定了。但是如果一个都不知道的话,就要算出每个数最大可能是多大。

举个例子,如果要使得a5最大,则令a4尽量小,a2尽量大,a1尽量小。如果要使得a4尽量大,则a2尽量小,a1尽量大。容易发现,使得a1尽量小和使得a1尽量大后满足所有等式的两排不同的数就是每个数的范围。

比如要使得a1尽量小,然后往下推出所有a[i]满足每个等式,a[n]就是满足所有等式的值了,然后往回推回去,就推出一排数了。然后推出使得a1尽量大的一排数,最后询问的时候直接输出两排数a[i]较大值。


code:

#include <stdio.h>

const int maxn = 100000 + 10;
int pre[maxn], next[maxn];
int a[maxn], b[maxn], c[maxn], sum[maxn];

int max(int a, int b)   { return a>b?a:b; }

//等式中相邻的未知数处理next和pre
void init(int n) {
    bool flag = 1;
    int i = 1;
    pre[1] = -1;
    while(i <= n) {
        if(flag) {
            next[i] = i+1;
            pre[i+1] = i;
            flag = 0;
            i += 1;
        }
        else {
            next[i] = i+2;
            pre[i+2] = i;
            i += 2;
            flag = 1;
        }
    }
}

int main() {
    init(100000);
    int n;
    while(scanf("%d", &n) != -1) {
        for(int i = 1;i <= n; i++)  scanf("%d", &a[i]);
        for(int i = 1;i <= n; i++)  scanf("%d", &sum[i]);
        a[0] = a[n+1] = 0;
        for(int i = 3;i <= n;i += 3) {
            a[i] = sum[i-1] - sum[i-2] + a[i-3];
        }
        bool flag = 0;
        // 如果n%3==0,则可以知道a[n-1]的值,就能直接推出所有的值
        if(n % 3 == 0) {
            for(int i = n-1;i >= 1; i--) if(a[i] == -1)
                a[i] = sum[i+1] - a[i+1] - a[i+2];
            flag = 1;
        }
        //如果n%3==1,就能知道a[n]的值,也可以推出所有的值
        else if(n % 3 == 1) {
            a[n] = sum[n] - a[n-1];
            for(int i = n-2;i >= 1; i--) if(a[i] == -1)
                a[i] = sum[i+1] - a[i+1] - a[i+2];
            flag = 1;
        }
        else {
            for(int i = 1;i <= n; i++) if((i % 3 != 0) && a[i] != -1) {
                //如果有一个未知数知道了,其他都可以确定了
                b[i] = a[i];
                for(int j = i-1;j >= 1; j--) if(a[j] == -1)
                    a[j] = sum[j+1] - a[j+1] - a[j+2];
                for(int j = i+1;j <= n; j++) if(a[j] == -1)
                    a[j] = sum[j-1] - a[j-1] - a[j-2];
                flag = 1;
                break;
            }
            if(!flag) {
                int now = 1;
                b[1] = 0; c[1] = sum[1];
                // b[i]:令a[1]尽量小的一排数
                // c[i]: 令a[1]尽量大的一排数
                while(now <= n) {
                    int ne = next[now];
                    if(ne - now == 2) {
                        b[ne] = sum[now+1] - a[now+1] - b[now];
                        c[ne] = sum[now+1] - a[now+1] - c[now];
                    }
                    else {
                        b[ne] = sum[ne] - a[ne+1] - b[now];
                        c[ne] = sum[ne] - a[ne+1] - c[now];
                    }
                    // 注意如果有个数为负数,不过值最小为0,不能为负的
                    if(b[ne] < 0)   b[ne] = 0;
                    if(c[ne] < 0)   c[ne] = 0;
                    now = ne;
                }
                now = n;
                for(int i = 0;i < n; i++)    b[i] = c[i] = a[i];
                for(int i = n-1;i >= 1; i--) if(b[i] == -1) {
                    b[i] = sum[i+1] - b[i+1] - b[i+2];
                    c[i] = sum[i+1] - c[i+1] - c[i+2];
                }
            }
        }
        int m, x;
        scanf("%d", &m);
        while(m--) {
            scanf("%d", &x);
            x++;
            if(flag)    printf("%d\n", a[x]);
            else {
                int ans = max(b[x], c[x]);
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}


你可能感兴趣的:(2013 ACM/ICPC Asia Regional Changsha Online - J Candies)