题意: 有一排数,有的明示它的值,有的则隐藏(以-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; }