题解:CF1270H Number of Components
题意:若 \(i < j, a_i < a_j\),则在 \(i,j\) 之间连边,定义一个序列权值为按上述方式连边后,数组中的连通块个数。现在每次对序列进行修改,同时输出序列权值。
做法:
对于这种看起来就很有性质的东西,我们一般来说先手玩一下观察性质,我们会发现,如果有一组 \((i,j)\) 满足条件,那么 \([i,j]\) 同属一个区间,证明的话直接分讨 \(a_k\) 的大小就行。
所以这个序列的连通块一定是分为 \([1,r_1],[l_2,r_2]\cdots [l_k, n]\) 这样的形式,等同于我们要做断点个数计数。
但是做到这里还不够,我们没有办法很方便的判断每个位置是否可以作为断点。
这里我们需要注意到第二个观察,一个位置 \(p\) 可以作为断点,当且仅当 \(\min\limits_{i=1}^p a_i > \max\limits_{i=p + 1}^n a_i\),这个比较显然,主要是直接猜的时候并不好观察出来。
做到这里还需要一个技巧,因为你直接维护前缀 \(\min\) 和后缀 \(\max\) 也很难快速处理出答案,我们考虑将答案进行一些转化。
我们枚举 \(\min\) 的值为 \(v\),将序列中 \(\ge v\) 的值标记为 \(1\),而 \(<v\) 的标记为 \(0\),那么我们会发现,如果序列如此标记后形如 \(11\cdots 100\cdots 0\),那么中间哪一个断点就是一个可行的位置,并且可以证明,如果我们枚举的 \(\min\) 都在序列中,那么不可能会有重复的断点,所以就等同于我们要计算有多少个在序列中的元素,满足标记后相邻的数不同的个数为 \(1\),这里为了方便统计,令 \(a_0 = \infty,a_{n+1}=-\infty\),并且将他们也进行标记。
那么之后就很简单了,我们考虑相邻的两个元素 \(a_p, a_{p+1}\)什么时候会出现 \(01,10\),当且仅当 \(v \in [\min({a_p}, a_{p+1}),\max(a_p,a_{p+1}))\) 的时候会,那么我们考虑用线段树维护每个 \(v\) 的答案即可。
如果求答案?注意到我们相邻不同的个数至少为 \(1\),所以我们在线段树节点上统计最小值及其个数即可,输出答案的时候注意特判最小值是否为 \(1\)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6 + 5, N = 1e6;
int n, dp[maxn], a[maxn], q;
struct node {
int mn, val;
node() {
mn = val = 0;
}
node(int V, int C) {
mn = V, val = C;
}
friend node operator+(node x, node y) {
node ans; ans.mn = min(x.mn, y.mn);
ans.val = x.val * (x.mn == ans.mn) + y.val * (y.mn == ans.mn);
return ans;
}
};
int use[maxn];
struct Segtree {
node tr[maxn << 2];
int tag[maxn << 2];
void pushup(int t) {
tr[t] = tr[t << 1] + tr[t << 1 | 1];
}
void build(int l, int r, int t) {
if(l == r) {
if(!use[l])
tr[t] = node((int)(1e9), 0);
else
tr[t] = node(tr[t].mn, 1);
return ;
}
int mid = l + r >> 1;
build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
pushup(t);
}
void addtag(int t, int val) {
tr[t].mn += val, tag[t] += val;
}
void pushdown(int t) {
addtag(t << 1, tag[t]);
addtag(t << 1 | 1, tag[t]);
tag[t] = 0;
}
void update(int l, int r, int x, int y, int t, int val) {
if(x <= l && r <= y) {
addtag(t, val);
return ;
}
int mid = l + r >> 1;
pushdown(t);
if(x <= mid)
update(l, mid, x, y, t << 1, val);
if(mid < y)
update(mid + 1, r, x, y, t << 1 | 1, val);
pushup(t);
}
void modify(int l, int r, int pos, int t) {
if(l == r) {
if(!use[l])
tr[t] = node((int)(1e9), 0);
else
tr[t] = node(tag[t], 1);
return ;
}
int mid = l + r >> 1;
pushdown(t);
if(pos <= mid)
modify(l, mid, pos, t << 1);
else
modify(mid + 1, r, pos, t << 1 | 1);
pushup(t);
}
void out(int l, int r, int t) {
if(l == r) {
if(tr[t].mn == 0)
cout << l << endl;
return ;
}
int mid = l + r >> 1;
// pushdown(t);
}
} tree;
void renew(int x, int val) {
int l = min(a[x], a[x + 1]), r = max(a[x], a[x + 1]);
tree.update(0, N, l, r - 1, 1, val);
}
signed main() {
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i], use[a[i]] = 1;
a[0] = 1e6 + 1, a[n + 1] = 0;
tree.build(0, N, 1);
for (int i = 0; i <= n; i++)
renew(i, 1);
// cout << tree.tr[1].mn << " " << tree.tr[1].val << endl;
while(q--) {
int p, v; cin >> p >> v;
renew(p, -1), renew(p - 1, -1);
use[a[p]] = 0; tree.modify(0, N, a[p], 1);
a[p] = v;
renew(p, 1), renew(p - 1, 1);
use[a[p]] = 1, tree.modify(0, N, a[p], 1);
// cout << tree.tr[1].mn << " " << tree.tr[1].val << endl;
if(tree.tr[1].mn == 1)
cout << tree.tr[1].val << endl;
else
cout << 1 << endl;
}
return 0;
}

浙公网安备 33010602011771号