博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2013 ACM/ICPC 长沙网络赛J题
阅读量:6200 次
发布时间:2019-06-21

本文共 3425 字,大约阅读时间需要 11 分钟。

题意:一个数列,给出这个数列中的某些位置的数,给出所有相邻的三个数字的和,数列头和尾处给出相邻两个数字的和。有若干次询问,每次问某一位置的数字的最大值。

分析:设数列为a1~an。首先通过相邻三个数字的和我们可以求出a3,a6,a9……是多少。a3=sum(a1,a2,a3)-sum(a1,a2)。a6=sum(a4,a5,a6)-sum(a3,a4,a5)。后面依次类推。

推到了数列的最右面,如果恰好知道了an或者a(n-1)中的一个,那么可以通过sum(an,a(n-1))减去它来求得另一个。

这个题还有个性质就是,只要知道数列中连续的两位就可以通过不断向两侧延伸的方法得到整个数列。因为任意相邻三个的和都知道,知道其中两个数自然可以得到第三个。

所以我们如果知道了最后两位则一定可以知道整个数列。同样如果根据已知的数列中的数和推出的数能得知两个已知的相邻的数的话,也可推出整个数列。

唯一一种无法确定该数列的情况就是我们,没有推出最后的两个数(即n%3==2的情况),且数列中已知给出的数也没有提供任何推出信息以外的信息。

这时我们就获得了一个不确定的数列,将数列中的数字ai按照i%3结果的不同分成三组,得0则已知。

得1的数之间互相是同增同减的,因为他们互相之间的差是一样的。a1-a4=sum(a1,a2,a3)-sum(a2,a3,a4)。得2的同理。所以这些同增同减的数字会同时取得最大值或最小值。

得1和得2的之间是此消彼涨的,因为他们互相之间的和是一样的。a1+a2=sum(a1,a2)    a2+a4=sum(a2,a3,a4)-a3    a4+a5=sum(a3,a4,a5)-a3 所以得1的取得最小值则得2的取得最大值,反之亦然。

在符合了这些和与差的条件之后,唯一会导致数列不合法的情况就是出现负数。我们只需要先对得1的随意娶个值,并推出整个数列后,根据最小数值调整所有得1的取值,使最小的那个数恰好为0。

这样就可以使得1的取最小值,即让得2的取得最大值。

同理可以求得得1的最大值。

存储好各种最大值之后按题目中的询问输出即可。计算最大值需要O(n),每次询问需要O(1),共m次询问,总复杂度为O(n + m)。

#include 
#include
#include
using namespace std;#define MAX_NUM 100005int array[MAX_NUM], array1[MAX_NUM], array2[MAX_NUM];int sum[MAX_NUM];int num;int query_num;bool filled;void cal_left(int pos, int array[]){ for (int i = pos - 2; i > 0; i--) array[i] = sum[i + 1] - array[i + 1] - array[i + 2];}void cal_right(int pos, int array[]){ for (int i = pos + 2; i <= num; i++) array[i] = sum[i - 1] - array[i - 1] - array[i - 2];}void input(){ int pos = -1; for (int i = 1; i <= num; i++) { scanf("%d", &array[i]); if (i % 3 != 0 && array[i] != -1) pos = i; } for (int i = 1; i <= num; i++) scanf("%d", &sum[i]); array[0] = 0; for (int i = 3; i <= num; i += 3) array[i] = sum[i - 1] - (sum[i - 2] - array[i - 3]); if (num % 3 == 2 && pos == -1) return; filled = true; if (array[num] != -1) array[num - 1] = sum[num] - array[num]; if (array[num - 1] != -1) array[num] = sum[num] - array[num - 1]; if (array[num] != -1) cal_left(num, array); array[num + 1] = 0; if (pos != -1) { if (array[pos - 1] != -1) pos--; cal_left(pos + 1, array); cal_right(pos, array); }}void work(){ scanf("%d", &query_num); for (int i = 0; i < query_num; i++) { int a; scanf("%d", &a); a++; if (filled) { printf("%d\n", array[a]); continue; } if (a % 3 == 1) printf("%d\n", array1[a]); else if (a % 3 == 2) printf("%d\n", array2[a]); else printf("%d\n", array[a]); }}void make2(){ memcpy(array2, array, sizeof(array2)); array2[1] = 0; array2[2] = sum[1] - array2[1]; cal_right(1, array2); array2[1] -= *min_element(array2 + 1, array2 + num + 1); array2[2] = sum[1] - array2[1]; cal_right(1, array2);}void make1(){ memcpy(array1, array, sizeof(array1)); array1[num] = 0; array1[num - 1] = sum[num] - array1[num]; cal_left(num, array1); array1[num] -= *min_element(array1 + 1, array1 + num + 1); array1[num - 1] = sum[num] - array1[num]; cal_left(num, array1);}int main(){ while (scanf("%d", &num) != EOF) { filled = false; input(); if (!filled) { make2(); make1(); } work(); } return 0;}
View Code

 

转载于:https://www.cnblogs.com/rainydays/p/3334641.html

你可能感兴趣的文章
判断一个链表是否有环
查看>>
jquery toastmessage (Jquery类似安卓消息提示框)
查看>>
Android:自己定义输入法(输入password时防止第三方窃取)
查看>>
python格式化字符串Type Error: Format Requires Mapping 的问题
查看>>
8.Java 加解密技术系列之 PBE
查看>>
c++中的数据类型
查看>>
CString 中的SpanIncluding 和SpanExcluding 用法
查看>>
[ACM] POJ 2253 Frogger (最短路径变形,每条通路中的最长边的最小值)
查看>>
自然数从何而来?
查看>>
C# 操作XML
查看>>
再次推荐一款逼真的HTML5下雪效果
查看>>
Redis集群_3.redis主从自动切换Sentinel(转)
查看>>
Storm内部的消息传递机制
查看>>
推荐 iOS 网站:
查看>>
***PHP 数组排序 +php二维数组排序方法(PHP比较器)
查看>>
COM中的几个基本概念
查看>>
Java事件总线
查看>>
Failed to create the java virtual machine完全解决办法
查看>>
MySQL使用位运算
查看>>
ubuntu16.4搭建tensorflow环境
查看>>