【 前缀和 单调双向队列 化环为链】P7590 回旋加速器(2021 CoE-II C)|普及+
本文涉及知识点
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
C++队列、双向队列
P7590 回旋加速器(2021 CoE-II C)
题目描述
回旋加速器( Cyclotron \text{Cyclotron} Cyclotron)是利用磁场和电场使带电粒子作回旋运动并经高频电场反复加速的装置,是高能物理中的重要仪器。
我们来研究回旋加速器的一个简化模型。将回旋加速器视为一个环形的轨道,轨道上设置了 n n n 个加速腔,依次编号为 1 1 1 至 n n n。将一束质子从某个加速腔导入,在导入时,质子束的动能为零。第 i i i 个加速腔能够为质子束提供 e i e_i ei 的动能 ,质子束从第 i i i 个加速腔运行到第 i + 1 i + 1 i+1 个加速腔会损失 d i d_i di 的动能(由于是环形轨道,编号为 n n n 的加速腔后面是编号为 1 1 1 的加速腔)。
给定每个加速腔能够提供的动能值以及质子束在各个加速腔之间运行所损失的动能值,试确定质子束能否绕环形轨道运行一周。如果能够成功,应该选择从哪个加速腔导入质子束。质子束在两个加速腔之间运行时,动能不能为零,但质子束刚到达加速腔时,动能可以为零,因为可以立即获得加速腔所提供的动能。
输入格式
输入包含多组测试数据。
输入第一行包含一个整数 T T T,表示测试数据的组数。接着是一个空行。
接下来是 T T T 组数据,每组数据由三行构成。两组数据之间有一个空行。
每组数据的第一行是一个整数 n n n,表示加速腔的个数。第二行一共 n n n 个整数,依次表示编号为 i i i 的加速腔能够提供的动能 e i e_i ei。第三行一共 n n n 个整数,依次表示质子束从第 i i i 个加速腔运行到第 i + 1 i + 1 i+1 个加速腔所损失的动能 d i d_i di。由于是环形轨道,第三行的第 n n n 个整数表示的是从第 n n n 个加速腔运行到第 1 1 1 个加速腔时损失的动能。
输出格式
每组数据输出一行。如果质子束无法环绕加速器运行一周,输出 Failed!
,否则输出导入质子束的加速腔编号,如果有多个加速腔可供选择,选择具有最小编号的加速腔。
输入输出样例 #1
输入 #1
13
1 2 3
2 3 4
输出 #1
Failed!
输入输出样例 #2
输入 #2
110
1 2 3 4 5 6 7 8 9 10
3 2 1 2 3 4 5 6 7 8
输出 #2
2
说明/提示
样例说明
输入 #1
该组输入共有 3 3 3 个加速腔,依次能够提供的动能为 1 1 1、 2 2 2、 3 3 3。从第 1 1 1 个加速腔运行到第 2 2 2 个加速腔损失 2 2 2 动能,从第 2 2 2 个加速腔运行到第 3 3 3 个加速腔损失 3 3 3 动能,从第 3 3 3 个加速腔运行到第 1 1 1 个加速腔损失 4 4 4 动能。不管从哪个加速腔导入质子束,都会使得质子束在两个加速腔运行过程中动能变为零,无法环绕轨道一周。
输入 #2
该组输入共有 10 10 10 个加速腔,如果从第 1 1 1 个加速腔导入质子束,将获得动能 1 1 1,但是在从第 1 1 1 个加速腔运行到第 2 2 2 个加速腔的过程中会损失 3 3 3 动能,因此会使得质子束无法环绕轨道一周。而从第 2 2 2 个到第 10 10 10 个加速腔中的任意一个导入质子束,均能保证质子束在加速腔之间运行时动能不为零,因此都可作为导入质子束的加速腔,但编号为 2 2 2 的加速腔具有最小的编号。需要注意,从第 2 2 2 个加速腔导入质子束,当运行到第 3 3 3 个加速腔时,动能恰为零,根据题意,这种情形是允许的。
数据范围
- Subtask 1 1 1: 2 ≤ n ≤ 10 2 \le n \le 10 2≤n≤10, 10 10 10 分。
- Subtask 2 2 2: 2 ≤ n ≤ 1 0 3 2 \le n \le 10^3 2≤n≤103, 30 30 30 分。
- Subtask 3 3 3: 2 ≤ n ≤ 1 0 5 2 \le n \le 10^5 2≤n≤105, 30 30 30 分。
- Subtask 4 4 4: 2 ≤ n ≤ 1 0 6 2 \le n \le 10^6 2≤n≤106, 30 30 30 分。
对于 100 % 100\% 100% 的数据, 1 ≤ T ≤ 20 1 \le T \le 20 1≤T≤20, 0 < e i ≤ 100 0 \lt e_i \le 100 0<ei≤100, 0 < d i ≤ 100 0 \lt d_i \le 100 0<di≤100。
约定
质子束的运行方向规定为:从第 1 1 1 个加速腔到第 2 2 2 个加速腔,从第 2 2 2 个加速腔到第 3 3 3 个加速腔 ⋯ \cdots ⋯从第 n n n 个加速腔到第 1 1 1 个加速腔。
前缀和 单调双向队列 化环为链
a[i]= e[i%N]-d[i%N],a有2N个元素。preSum是a的前缀和。枚举结尾 i ∈ [ 0 , n ) i \in[0,n) i∈[0,n),求对应的最佳首下标 j ∈ [ 0 , i ] j\in[0,i] j∈[0,i]。如果有多个符合条件,则求最小j:
∀ i 1 ∈ [ j , i ] \forall i1 \in [j,i] ∀i1∈[j,i],e[j…i1]都必须大于等于0。即preSum[i1+1]-preSum[j] >=0。如果preSum[j] > preSum[i+1],则j不是i及更大下标的解。
可永久删除(操作一)。j1<j2,如果preSum[j1] < preSum[j2],则j1一定劣于j2。如果j2是i的解,则j1也是,且j1更小。淘汰j2后(操作二),preSum[j]降序。
操作一:队首比较、出队。操作二:队尾判断是否需要入队,队尾入队。
代码
核心代码
#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行 return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错 }m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};class Solution {
public:int Ans(const int N, vector<int>& e, vector<int>& d) {vector<int> preSum(2 * N + 1);for (int i = 0; i < 2 * N; i++) {preSum[i + 1] = preSum[i] + e[i % N] - d[i % N];}deque<int> que;for (int i = 0; i < 2 * N; i++) {while (que.empty() || (preSum[que.back()] > preSum[i])) {que.emplace_back(i);}while (que.size() && (preSum[que.front()] > preSum[i + 1])) {que.pop_front();}if (que.size() && (i + 1 - que.front() >= N)) {return que.front() + 1;}}return -1;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG ios::sync_with_stdio(0); cin.tie(nullptr);CInBuff<10'000'000> in;int T,N; in >> T;for (int i = 0; i < T; i++){in >> N ;auto e = in.Read<int>(N);auto d = in.Read<int>(N);
#ifdef _DEBUG //printf("N=%d", N);//Out(e, "e=");//Out(d, "d=");//Out(B, "B=");//Out(strs2, ",strs2=");//Out(que, ",que=");/*Out(que, "que=");*/
#endif // DEBUG auto res = Solution().Ans(N,e,d);cout << ((-1==res)?"Failed!" : to_string(res).c_str()) << "\n";}return 0;
}
单元测试
int N ;vector<int> e, d;TEST_METHOD(TestMethod1){N=3,e = { 1,2,3 }, d = { 2,3,4 };auto res = Solution().Ans(N, e, d);AssertEx(-1, res);}TEST_METHOD(TestMethod2){N = 10,e = { 1,2,3,4,5,6,7,8,9,10 },d = { 3,2,1,2,3,4,5,6,7,8 };auto res = Solution().Ans(N, e, d);AssertEx(2, res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。