本文涉及知识点
C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频
C++DFS
C++并集查找
P3127 [USACO15OPEN] Trapped in the Haybales G
题目描述
Farmer John 收到了一批 N N N 个大型干草捆( 1 ≤ N ≤ 100 , 000 1 \le N \le 100,000 1≤N≤100,000),并将它们放置在他通往谷仓的道路上的不同位置。不幸的是,他完全忘记了奶牛 Bessie 正在这条路上吃草,她现在可能被困在这些干草捆之间了!每个干草捆 j j j 有一个大小 S j S_j Sj 和一个位置 P j P_j Pj,表示它在这条一维道路上的位置。Bessie 可以在道路上自由移动,甚至可以移动到干草捆所在的位置,但她无法穿过这个位置。唯一的例外是,如果她朝同一方向连续移动 D D D 单位的距离,她将获得足够的速度,能够突破并永久消除任何大小严格小于 D D D 的干草捆。当然,在突破之后,她可能会打开更多的空间,从而有机会突破其他干草捆,并继续消除它们。
如果 Bessie 最终能够突破最左侧或最右侧的干草捆,她就可以成功逃脱。请计算道路中所有无法逃脱的实数起始位置的总面积。
输入格式
输入的第一行包含 N N N。接下来的 N N N 行描述每个干草捆,每行包含两个整数,分别表示干草捆的大小和位置,范围均为 1 ... 1 0 9 1 \ldots 10^9 1...109。所有位置均不相同。
输出格式
输出一个整数,表示 Bessie 无法逃脱的道路总面积。
输入输出样例 #1
输入 #1
5
8 1
1 4
8 8
7 15
4 20
输出 #1
14
BFS( 错误 错误 错误)
性质一 :n堆干草,将整个道路分成n-1区间。任何区间,只要有一个点能够突破所有干草堆,则整个区间能突破所有干草堆。以向右突破为例,本区间所有点先向左到左端点后,再向右。
0 ≤ i ≤ N − 2 0\leq i \le N-2 0≤i≤N−2 r[i]记录到达第i堆干草至少已经向右r[i],才能向右突破所有草堆。
r[i] =max(r[i+1]-此区间长度,s[i]-此区间长度)
如果r[i]<0,可以做起点。
类似:left[i]记录到达第i+1堆干草,至少已经向左left[i],才能向左突破所有草堆。
如果区间left[i]<0或r[i]<0,则称此区间能够直接突破。如果没有区间能直接突破,则所有区间不能突破。
性质二 :区间一和区间二相邻,如果区间一为起点能够到达区间二,则区间二能够突破所有干草,则区间一也能。即一个连通区域只要有一个点能突破,则连通区域所有点都能突破。注意 :相邻是有向的。
实现:边反向后,以直接突破的区间为起点,BFS。
错误原因 :存在以下情况: 区间一 → 区间二 → 区间三 区间一\rightarrow 区间二 \rightarrow 区间三 区间一→区间二→区间三,但区间二无法到达区间三。
暴力解法:枚举所有边而不是相邻边。时间复杂度:O(nn) 时间超限。
BFS+虚拟节点+前缀和(错误)
根据上述分析,我们可以得出如下结论。
性质一 :一个区间可以缩成一个点,共n-1个点,增加一个虚拟节点n-1,共n个节点。
性质二 :所有能直接突破的区间对应的点连向n-1。
如果是BFS,不需要虚拟节点。
增加区间间的边
以向右的边( i < j < k i<j<k i<j<k)为例。从i能够到达k,且不能到达k+1。向左的边类似。
性质三 :如果j能够到达k,则只需要 i → j → k i \rightarrow j \rightarrow k i→j→k,无需 i → k i \rightarrow k i→k
性质四 :如果i不能到达j,则i也无法到达k。
小结一 :如果 j → k j \rightarrow k j→k,则除j外,不需要任意其它边连向k。
性质五 :如果i不能到达 k + 1 k+1 k+1,则一定不能到达 k + 2 k+2 k+2。
栈sta记录所有 ∀ j \forall j ∀j都不能到达的k,从栈底到栈顶降序。当i能到达栈顶,连边出栈。
判断i能否到达栈顶:
区间i到栈顶的距离 是否大于 栈顶S。
错误原因 :
区间一无法到达区间二,区间二无法直接到达区间三,但 区间 2 → 区间 1 → 区间 3 区间2 \rightarrow 区间1 \rightarrow 区间3 区间2→区间1→区间3却成立。
因为干草堆是永远消失。
错误代码
cpp
#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 COutBuff
{
public:
COutBuff() {
m_p = puffer;
}
template<class T>
void write(T x) {
int num[28], sp = 0;
if (x < 0)
*m_p++ = '-', x = -x;
if (!x)
*m_p++ = 48;
while (x)
num[++sp] = x % 10, x /= 10;
while (sp)
*m_p++ = num[sp--] + 48;
AuotToFile();
}
void writestr(const char* sz) {
strcpy(m_p, sz);
m_p += strlen(sz);
AuotToFile();
}
inline void write(char ch)
{
*m_p++ = ch;
AuotToFile();
}
inline void ToFile() {
fwrite(puffer, 1, m_p - puffer, stdout);
m_p = puffer;
}
~COutBuff() {
ToFile();
}
private:
inline void AuotToFile() {
if (m_p - puffer > N - 100) {
ToFile();
}
}
char puffer[N], * m_p;
};
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:
long long Ans(vector<pair<int, int>>& sp) {
const int N = sp.size();
sort(sp.begin(), sp.end(), [&](const auto& p1, const auto& p2) {return p1.second < p2.second; });
vector<long long >left(N ), r(N ),vLen(N);
for (int i = N - 2; i >= 0; i--) {
vLen[i] = sp[i + 1].second - sp[i].second;
r[i] = max((long long)sp[i + 1].first, r[i + 1] ) - vLen[i];
}
vector<long long> preSum = { 0 };
for (const auto& i : vLen) {
preSum.emplace_back(preSum.back() + i);
}
long long ans = 0;
queue<int> que;
vector<bool> vis(N - 1);
auto Add = [&](int cur) {
if (vis[cur]) { return; }
vis[cur] = true;
que.emplace(cur);
ans -= vLen[cur];
};
for (int i = 0; i + 1 < N; i++) {
if (0 == i) {
left[i] = sp[i].first - vLen[i];
}
else {
left[i] = max( (long long)sp[i].first,left[i-1]) - vLen[i];
}
if ((left[i] < 0) || (r[i] < 0)) { Add(i); }
}
vector<vector<int>> neiBoBack(N - 1);
{//处理向右的边
stack<int> sta;
for (int i = N - 2; i >= 0; i--) {
while (sta.size()&&( preSum[sta.top()]- preSum[i] > sp[sta.top()].first))
{
neiBoBack[sta.top()].emplace_back(i);
sta.pop();
}
sta.emplace(i);
}
}
{//处理向左的边
stack<int> sta;
for (int i = 0; i + 1 < N; i++) {
while(sta.size()&&(preSum[i+1]-preSum[sta.top()+1] > sp[sta.top()+1].first))
{
neiBoBack[sta.top()].emplace_back(i);
sta.pop();
}
sta.emplace(i);
}
}
while (que.size()) {
const auto cur = que.front(); que.pop();
for (const auto&next : neiBoBack[cur]) {
Add(next);
}
}
return ans+ preSum.back();
}
};
int main() {
#ifdef _DEBUG
freopen("a.in", "r", stdin);
#endif // DEBUG
ios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);
auto sp = Read<pair<int, int>>();
#ifdef _DEBUG
//printf("N=%d", N);
Out(sp, ",sp=");
//Out(que, ",=que");
#endif // DEBUG
auto res = Solution().Ans(sp);
cout << res << "\n";
return 0;
}
并集查找
令某区间i,不转向向左能到left,向右能到r。此时left到r之间的干草堆已经被消除,故可以看成一个大区间。
left,r\]不转向最左能够到达left1,最右能够到达r1。如果 l e f t = = l e f t 1 且 r = = r 1 left == left1 且 r == r1 left==left1且r==r1则迭代结束。
否则 l e f t = l e f t 1 , r = r 1 left=left1,r=r1 left=left1,r=r1继续迭代。
如果最终 l e f t \< 0 或 r \> N − 2 left \< 0或r \>N-2 left\<0或r\>N−2则区间i能够突破所有。
**性质一** :如果i不能突破,则\[i,r\]都不能突破。区间i到达区间k 速度 ≥ 0 速度\\ge0 速度≥0,从区间k出发速度0。
**性质二**:同理i能到达j,j能到达的区间,则i也能到达。left\[j\]记录j能到达的最左点,如果i能到达j,则能到达left\[j\] ,left\[left\[j\]\],我们用并集查找记录。
### 超时代码
```c
class Solution {
public:
long long Ans(vector