【问题描述】
一张长度为N的纸带,我们可以从左至右编号为0 - N(纸带最左端标号为
0) 。现在有M次操作,每次将纸带沿着某个位置进行折叠,问所有操作之后纸带 的长度是多少。
【输入格式】
第一行两个数字N,M如题意所述。 接下来一行M个整数代表每次折叠的位置。
【输出格式】
一行一个整数代表答案。
【样例输入】
5 2
3 5
【样例输出】
2
【数据规模与约定】
对于60%的数据,N,M ≤ 3000。 对于100%的数据,N ≤ 10^18,M ≤ 3000。
首先不要被数据范围吓到, 其实就是一个简单地模拟了.
由于数据很大, 有10^18, 考虑使用 unsigned long long
我开始考虑的链状结构来存储纸条, 结果可想而知了(╮(╯▽╰)╭), 其实不要把它当做真的纸条就好了, 我们考虑使用指针
(我用的”真指针”, 你们随意, ╮(╯▽╰)╭), 申请 左指针 和 右指针 来标明区间, 并且用这两个指针来进行编号的变换:当纸条折叠时, 我们需要在新序列中指明其编号.
申请数组 F[] 保存M个变换的轴点, 更新编号时就是修改F[].
有如下两条规律:
我们向”纸条长”的一方折叠, 比如序列 0 1 2 3 4 5 , 以3为轴点折叠, 则向左折叠;以2为轴点就向右折叠.
代码很简单, 看看就懂
/*
* Copyright © Eliot
* Date: 2016-11-05
* Author: Eliot
* QQ: 1161142536
* Description: 技巧模拟
*/
#include <iostream>
#include <cstdio>
using namespace std;
const int MAX = 3005;
unsigned long long A[MAX] = { 0 }, n = 0;
int m = 0;
int main() {
freopen("he.in", "r", stdin);
freopen("he.out", "w", stdout);
scanf("%I64d %d", &n, &m);
unsigned long long* pLeft = new unsigned long long(0);
unsigned long long* pRight = new unsigned long long(n);
for (int i = 1; i <= m; i++) scanf("%I64d", &A[i]);
for (int i = 1; i <= m; i++) {
if (A[i] * 2 >= *pLeft + *pRight) {
pRight = &A[i];
}
else {
pLeft = &A[i];
}
for (int j = i + 1; j <= m; j++) {
if (A[j] > *pRight) A[j] = *pRight * 2 - A[j];
if (A[j] < *pLeft) A[j] = *pLeft * 2 - A[j];
}
}
cout << *pRight - *pLeft << endl;
pRight = 0;
pLeft = 0;
delete pRight;
delete pLeft;
return 0;
}