【2020ICPC·小米 网络选拔赛第一场-E】Phone Network
作者:互联网
题目链接:https://ac.nowcoder.com/acm/contest/7501/E
题目大意
给定\(n\)个数的序列,对于每一个\(m\),求最短的涵盖从\(1\)到\(i\)的线段的长度。
思路
看了题解之后只剩下:妙啊。
题解已经说得很清楚了,这里复读一遍重新整理一下自己的思路。
\(R_{i,l}\)代表以\(l\)为线段左端点,包含数从\(1\)~\(i\)中所有数字的最近右端点。
对于\(i+1\)来说,其所在的位置为\(p_{1},p_{2},p_{3},...,p_{k}\)。这些数将长度为\(n\)的区间划分为若干部分,如下所示:
[1, \(p_{1}\)],[\(p_{1}+1\), \(p_{2}\)],...,[\(p_{k}+1\),\(n\)]
对于每一个区间,其内部所有的 \(l\) 在从 \(R_{i,l}\) 转移至 \(R_{i+1,l}\) 时,式子为\(R_{i+1,l}\) = \(max(R_{i,l}, \ p_{k})\)
其中,\(p_{k}\)代表其所在区间的右端点。
特别的,当所在的 \(l\) 位于最后一个线段中,不可能构成包含数从 \(1\)~\(i\) 中所有数字的线段,此时应将 \(R_{i+1,l}\) 置为无穷大,也就是取不到
显然在任何情况下 \(R_{i,l}\) 是单调递增的。最后,将问题转移成了对于每一个区间,找到 \(R_{i,l} < p_{k}\) 的一段数并将其值赋为 \(p_{k}\) 。这个操作可以利用线段树进行维护。
线段树维护过程:
用结构体存储线段树每个节点的左端点和右端点,当被赋值时,因为时区间同时赋值,因此所在区间的最短线段的长度记为 \(val - T[rt].r + 1\) 。
在查询 \(R_{i,l} < p_{k}\) 的左端点时尽可能地往左边走,在查询右端点时尽可能地往右边走,如果整个区间内没有符合的情况则跳过赋值。
AC代码
#include <bits/stdc++.h>
#define SZ(x) (int)x.size()
#define pii pair<int, int>
#define mp make_pair
#define inf 0x3f3f3f3f
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 5;
int init_val[MAXN];
class SEG {
public:
struct node {
int l, r;
int val, minn; // val是维护的右端点,minn是线段长度
} T[MAXN << 2];
int lazy[MAXN << 2];
inline void push_up(int rt) {
T[rt].minn = min(T[rt << 1].minn, T[rt << 1 | 1].minn);
T[rt].val = min(T[rt << 1].val, T[rt << 1 | 1].val);
}
void build(int rt, int l, int r) {
T[rt].l = l, T[rt].r = r;
if (l == r) {
T[rt].minn = init_val[l] - r + 1;
T[rt].val = init_val[l];
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid), build(rt << 1 | 1, mid + 1, r);
push_up(rt);
}
inline void push_down(int rt) {
if (lazy[rt]) {
T[rt << 1].val = lazy[rt], T[rt << 1 | 1].val = lazy[rt];
T[rt << 1].minn = lazy[rt] - T[rt << 1].r + 1, T[rt << 1 | 1].minn = lazy[rt] - T[rt << 1 | 1].r + 1;
lazy[rt << 1] = lazy[rt], lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = 0;
}
}
int query_left(int rt, int L, int R, int v) {
if (T[rt].r < L || T[rt].l > R) return -1;
if (T[rt].l == T[rt].r) return T[rt].l;
push_down(rt);
int ans = -1;
if (T[rt << 1].val < v) ans = query_left(rt << 1, L, R, v);
if (ans == -1 && T[rt << 1 | 1].val < v) ans = query_left(rt << 1 | 1, L, R, v);
return ans;
}
int query_right(int rt, int L, int R, int v) {
if (T[rt].r < L || T[rt].l > R) return -1;
if (T[rt].l == T[rt].r) return T[rt].l;
push_down(rt);
int ans = -1;
if (T[rt << 1 | 1].val < v) ans = query_right(rt << 1 | 1, L, R, v);
if (ans == -1 && T[rt << 1].val < v) ans = query_right(rt << 1, L, R, v);
return ans;
}
void change(int rt, int L, int R, int v) {
if (L <= T[rt].l && T[rt].r <= R) {
T[rt].val = v;
T[rt].minn = v - T[rt].r + 1;
lazy[rt] = v;
return;
}
push_down(rt);
int mid = (T[rt].l + T[rt].r) >> 1;
if (L <= mid) change(rt << 1, L, R, v);
if (R > mid) change(rt << 1 | 1, L, R, v);
push_up(rt);
}
} tree;
int a[MAXN];
vector<int> vec[MAXN];
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) vec[a[i]].pb(i);
int pos = inf;
for (int i = n; i >= 1; i--) {
if (a[i] == 1) pos = i;
init_val[i] = pos;
}
tree.build(1, 1, n);
printf("%d", tree.T[1].minn); // m = 1
for (int i = 2; i <= m; i++) {
for (int j = 0; j < SZ(vec[i]); j++) {
int l, r, p = vec[i][j];
if (j == 0) { // segment no 1
l = 1, r = vec[i][j];
} else l = vec[i][j - 1] + 1, r = vec[i][j];
int left = tree.query_left(1, l, r, p);
int right = tree.query_right(1, l, r, p);
if (left == -1 && right == -1) {
} else {
tree.change(1, left, right, p);
}
}
if (vec[i][SZ(vec[i]) - 1] == n) {}
else {
tree.change(1, vec[i][SZ(vec[i]) - 1] + 1, n, inf); // 忘记+1导致debug了好久
}
printf(" %d", tree.T[1].minn);
}
}
附上一组样例:
10 4
1 1 2 3 1 2 1 4 1 4
标签:rt,Network,val,int,线段,Phone,2020ICPC,端点,define 来源: https://www.cnblogs.com/tudouuuuu/p/13878728.html