【题解】CodeForces 2163 C Monopati
题目链接
题目大意
给定一个 \(2\) 行 \(n\) 列的数组 \(a\),每个元素 \(a_{i,j}\) 均为整数且属于 \([1, 2n]\) , 对于给定的 \(1 \leq l \leq r \leq 2n\) , 构造数组 \(b\) 若 \(l \leq a_{i,j} \leq r\) 则, \(b_{i,j} = 1\) 否则\(b_{i,j} = 0\), 若存在一条仅向右或向下走的路径使得 \((1, 1)\) 能够到达 \((2, n)\) , 且经过的每一个 \(b_{i,j}\) 均为 1 ,则称 这一对给定的 \((l, r)\) 合法。
问有多少组合法的 \((l, r)\) 。
思路
经过简单观察,我们可以发现如下规律:
- 如果 \((l, r)\) 合法,\((l, r+1)\) 一定合法,如果 \((l, r)\) 不合法, \((l+1, r)\) 一定不合法。
- 对于给定 \(l\) ,我们称最小的合法 \(r\) 为 \(r_l\), 左边界为 \(l\) 的所有合法方案数为 \(2n+1 - r_l\) ,为方便后续处理,我们记当无论如何都无法得到合法方案时 \(r_l=2n+1\),即调整合法方案为0。
- 对于一个确定的 \((l, r)\) 我们记第一行所有出现的 \(0\) 的位置的集合为 \(l_1\) , 第二行所有出现的 \(0\) 的位置的集合为 \(l_2\) , 当 \(min(l_1) > max(l_2) + 1\) 时,答案合法。这个结论正向不容易想到, \(min(l_1)\) 第一行出现的第一个 0 的位置,之前全是1, \(max(l_2)\) 是第二行出现的最后一个 0 的位置,之后全是1,如果 \(min(l_1) > max(l_2) + 1\) , 则存在合法解。
- 从 \((l, r)\) 转移至 \((l, r+1)\) ,只有 \(\{b_{i,j}| a_{i,j} = r+1\}\) 中的元素可能发生变化,其余部分不变, 从 \((l, r)\) 转移至 \((l+1, r)\) 同理。
显然,这是一个双指针法,然后我们用两个set维护两行中出现的0即可。
如何维护呢?
- 输入时开 \(2n\) 个vector记录,第 \(k\) 个vector存 \(\{(i,j) | a_{i,j} = k\}\) , 我的代码中分第一第二行两个vector避免出现pair。
- 初始状态,所有元素都是0,加入 \(l_1\) \(l_2\),我的代码中还加入了两个哨兵节点避免后续处理set空的情况。
- 从 \((0, 0)\) 开始
从 \((l, r)\) 转移至 \((l, r+1)\)时,把 \(\{b_{i,j}| a_{i,j} = r+1\}\) 中的所有 \(b_{i,j}\) erase 掉,重复执行直到存在合法解,此时得到的 \(r\) 即为 \(r_l\)。
从 \((l, r)\) 转移至 \((l+1, r)\)时,把 \(\{b_{i,j}| a_{i,j} = l+1\}\) 中的所有 \(b_{i,j}\) 重新insert,重复执行直到解不合法,期间不断在 \(ans\) 上累加 \(2n+1 - r\)。
检查退出循环
然后这题就完成了。
AC代码
\(ans\) 最好开 \(long \ long\),没试过,但是可能会炸。
可以统计出现的最小最大值来优化常数。
#include <iostream>
#include <vector>
#include <set>
using namespace std;
const int N = 200005;
int a[2][N];
vector<int> inv[2][2*N];
set<int > l1, l2;
int n;
bool check() {
if(*l1.begin()>*l2.rbegin()+1) {
return true;
} else {
return false;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T;
cin>>T;
while(T--) {
cin>>n;
int mini = 0x3f3f3f3f;
int maxi = 0;
l1.clear();
l2.clear();
for(int i=1;i<=2*n;i++) {
inv[0][i].clear();
inv[1][i].clear();
}
for(int i=1;i<=n;i++) {
cin>>a[0][i];
maxi = max(maxi, a[0][i]);
mini = min(mini, a[0][i]);
inv[0][a[0][i]].push_back(i);
}
for(int i=1;i<=n;i++) {
cin>>a[1][i];
maxi = max(maxi, a[1][i]);
mini = min(mini, a[1][i]);
inv[1][a[1][i]].push_back(i);
l1.insert(i);
l2.insert(i);
}
l1.insert(n+1);
l2.insert(0);
int l=1, r = mini-1; // l不合法下界,r合法下界
long long ans = 0;
while(true) {
while(r<maxi && !check()) {
++r;
for(int i:inv[0][r]) {
l1.erase(i);
}
for(int i:inv[1][r]) {
l2.erase(i);
}
}
if(!check()) {
break;
}
while(l<=a[0][1] && check()) {
ans += 2*n-r+1;
for(int i:inv[0][l]) {
l1.insert(i);
}
for(int i:inv[1][l]) {
l2.insert(i);
}
l++;
}
}
cout<<ans<<endl;
}
}
单组数据时间复杂度 \(O(n log n)\) ,每个元素最多被erase并insert一次,每次处理会由于set带来不大于 \(log n\) 的复杂度。

浙公网安备 33010602011771号