现在的位置: 首页 > 综合 > 正文

2013 ACM/ICPC Asia Regional Changsha Online – J Candies

2013年10月11日 ⁄ 综合 ⁄ 共 2094字 ⁄ 字号 评论关闭

题意: 有一排数,有的明示它的值,有的则隐藏(以-1表示)。现在给出他们所有相邻三个的和(头尾两个位置只包含两个数的和),有500次询问,问某个位置上的可能最大值是多少。

解法: 容易知道我们可以把从左到右数第三个数的值求出,类推我们能够知道所有位置是3的倍数的值,再类推我们能够知道所有从右往左数位置是3的倍数的值。所以当n%3!=2时,我们能够知道每三个数中的两个的值,剩下的一个可以直接求出。而当n%3==2时,我们就只能算出所有3的倍数位置的值,但是因为有的值是之前已经明示过的,因此只要有任意一个明示的值位置%3!=0,那么就有两个已知值是连续的,而我们知道连续三个数的和,因此可以推出所有的值。

还有一种情况,没有任何两个连续数已知,那么就是这样:xxoxxoxxoxx,o表示能确定的值,x表示未知数。于是我们把已知的三连续数和减去已知的数,处理成相邻两个未知数的和的形式,那么就会得到(n-n/3-1)个二元一次方程。不妨把方程写成下面这种形式:

x2 = y1 - x1

x3 = y2 - x2

x4 = y3 - x3

x5 = y4 - x4

x6 = y5 - x5

.

.

.

xn = yn - xn-1

再不妨把所有未知数x用x1替代

x2 = y1 - x1

x3 = y2 - y1 + x1

x4 = y3 - y2 + y1 - x1

x5 = y4 - y3 + y2 - y1 + x1

x6 = y5 - y4 + y3 - y2 + y1 - x1

.

.

.

xn = delta (+-) x1  >= 0

把所有方程写成如上一行所示的形式,题目的条件是不存在负数且小于10000,那么就变成关于x1的一些不等式,借此算出x1的上界和下界,从而根据每个未知数与x1的关系来求出他们的最大值,并且保证了每一个等式和不等式的合法性。

最后一步逗逼了4.5个小时,纪念一下。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAXN = 101000;
const int INF = 0x3f3f3f3f;
int n,ok,pos;
int a[MAXN];
int s[MAXN];
int min_lim,max_lim;
int delta[MAXN];
void Soul() {
    min_lim = 0;
    max_lim = 10000;
    for (int i = 3; i <= n; i += 3) {
        s[i-1] -= a[i];
        s[i] -= a[i];
        s[i+1] -= a[i];
    }
    for (int i = 2; i <= n; i ++) {
        if (i%3 == 1) {
            delta[i] = s[i-1] - delta[i-2];
            min_lim = max(min_lim, -delta[i]);
        }
        else if (i%3 == 2) {
            delta[i] = s[i] - delta[i-1];
            max_lim = min(max_lim, delta[i]);
        }
    }
    for (int i = 1; i <= n; i ++) {
        if (i%3 == 1) 
            a[i] = delta[i] + max_lim;
        else if (i%3 == 2)
            a[i] = delta[i] - min_lim;
    }
}
void work() {
    int temp = s[1];
    for (int i = 3; i <= n; i += 3) {
        a[i] = s[i-1] - temp;
        temp = s[i+1] - a[i];
    }
    temp = s[n];
    for (int i = n-2; i >= 1; i -= 3) {
        a[i] = s[i+1] - temp;
        temp = s[i-1] - a[i];
    }
    if (n%3 != 2) {
        for (int i = 1; i <= n; i ++) if (a[i] != -1) {
            s[i-1] -= a[i];
            s[i] -= a[i];
            s[i+1] -= a[i];
        }
        for (int i = 1; i <= n; i ++) if (a[i] == -1)
            a[i] = s[i];
        return ;
    }
    if (ok) {
        if (pos % 3 == 1) {
            for (int i = pos+1; i <= n; i ++) a[i] = s[i-1] - a[i-1] - a[i-2];
            for (int i = pos-1; i >= 1; i --) a[i] = s[i+1] - a[i+1] - a[i+2];
        } else {
            for (int i = pos-1; i >= 1; i --) a[i] = s[i+1] - a[i+1] - a[i+2];
            for (int i = pos+1; i <= n; i ++) a[i] = s[i-1] - a[i-1] - a[i-2];
        }
        return ;
    }
    Soul();
}
int main() {
    while (~scanf("%d", &n)) {
        memset(a, 0, sizeof(a));
        memset(s, 0, sizeof(s));
        memset(delta, 0, sizeof(delta));
        ok = 0;
        for (int i = 1; i <= n; i ++) {
            scanf("%d", &a[i]);
            if (!ok && a[i] != -1 && i%3 != 0) ok = 1, pos = i;
        }
        for (int i = 1; i <= n; i ++)
            scanf("%d", &s[i]);
        work();
        int nq;
        scanf("%d", &nq);
        while (nq --) {
            int d;
            scanf("%d", &d);
            d ++;
            printf("%d\n", a[d]);
        }
    }
    return 0;
}

抱歉!评论已关闭.