5 Nov 2016 本文阅读量

【智障模拟赛】 智障的折纸条

【问题描述】 
一张长度为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为轴点就向右折叠.

  • 纸条向右折叠时, 更新编号 F[i] = pRight(新区间) * 2 - F[i]
  • 纸条向左折叠时, 更新编号 F[i] = pLeft(新区间) * 2 - F[i]

代码很简单, 看看就懂

/*
* 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;
}

Tags:
Status:

Share:

Comments: