专栏文章
拜年【题解】
P15361题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mlq3m6g6
- 此快照首次捕获于
- 2026/02/17 12:23 前天
- 此快照最后确认于
- 2026/02/17 12:23 前天
拜年【题解】
这题正解是线性的,不过考虑到这是一场基础赛和管理员的建议,我们把 做法放过去了。
我们要统计满足条件的 的数量,使得:
- 把所有 的格子变成 ,其余变成 。
- 在这个二进制网格中,从 到 存在一条只经过 的四连通路径。
双指针
可以枚举 ,求对于固定的 有多少个 满足条件。有一个显然的性质:若 满足条件,则 一定满足条件,因为 相对于 ,值域增大,为 的格子会增多,连通性不减弱。
因此,对于一个固定的 ,如果存在合法的 ,则一定可以找到一个区间 ,满足当且仅当 时合法。
此时可以尝试维护第一个不合法的 。又可以发现,若 增加 , 是单调不降的,因此如果能够 的实现 扩展, 收缩,以及查询 和 是否连通就可以双指针实现 的复杂度。
刻画充要条件
注意到本题只有 2 行,结构非常特殊,可以刻画“是否连通”的充要条件,从而做到线性或近线性。
对于一个固定的 ,对应的二进制网格为 。
我们关心的是:在 的网格上,什么时候 可以到达 。
可以证明,其连通当且仅当同时满足:
-
起点与终点是 。即:
- 是 。
- 是 。
-
每一列至少有一个 。如果没有,路径被直接切断。
-
不存在“交叉阻断”结构。“交叉阻断”就是说,存在相邻两列:
- 两列都恰好只有一个 。
- 且这两个 在不同的行。
形如:CPP1 0 0 1 0 1 1 0这种结构会把路径“剪断”,形成左右不连通的两个块。
代码中的变量含义是:
| 变量 | 含义 |
|---|---|
flag | 起点与终点中有几个满足都是 |
cmk | 多少列含有至少一个 |
cnt | “交叉阻断”结构的数量 |
路径存在等价于:
CPPflag == 2 && cmk == n && cnt == 0
复杂度
最终 。
AC 代码
CPP#include<bits/stdc++.h>
using namespace std;
struct node {
int t, p;
};
inline bool check(const node& chk, int n) { return (chk.t == 0 && chk.p == 1) || (chk.t == 1 && chk.p == n); }
inline int ct(int i, int j, const vector<vector<int>>& mp, int n) {
if (i < 1 || j > n) return 0;
return (mp[0][i] + mp[1][i] == 1) && (mp[0][j] + mp[1][j] == 1) && (mp[0][i] != mp[0][j]);
}
int chkadd(const node& x, vector<vector<int>>& mp, int n) {
int res = -ct(x.p - 1, x.p, mp, n) - ct(x.p, x.p + 1, mp, n);
mp[x.t][x.p] = 1;
return res + ct(x.p - 1, x.p, mp, n) + ct(x.p, x.p + 1, mp, n);
}
int chkdel(const node& x, vector<vector<int>>& mp, int n) {
int res = -ct(x.p - 1, x.p, mp, n) - ct(x.p, x.p + 1, mp, n);
mp[x.t][x.p] = 0;
return res + ct(x.p - 1, x.p, mp, n) + ct(x.p, x.p + 1, mp, n);
}
void solve() {
int n;
cin >> n;
vector<vector<int>> a(2, vector<int>(n + 1)), mp(2, vector<int>(n + 1));
vector<vector<node>> find(2 * n + 1);
vector<int> mk(n + 1);
for (int i = 1; i <= n; i++) cin >> a[0][i], find[a[0][i]].push_back({0, i});
for (int i = 1; i <= n; i++) cin >> a[1][i], find[a[1][i]].push_back({1, i});
int l = 1, cmk = 0, cnt = 0, flag = 0;
long long ans = 0;
for (int r = 1; r <= 2 * n; r++) {
for (const auto &i : find[r]) {
cnt += chkadd(i, mp, n);
if (++mk[i.p] == 1) cmk++;
flag += check(i, n);
}
while (l <= r && (flag == 2 && cmk == n && cnt == 0)) {
for (const auto& i : find[l]) {
cnt += chkdel(i, mp, n);
if (--mk[i.p] == 0) cmk--;
flag -= check(i, n);
}
l++;
}
ans += l - 1;
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
int t;
cin >> t;
while (t--) solve();
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...