原题链接
看到\(\operatorname{mex}\),一个显然的想法就是根号分治。我们设\(d_u\)为点\(u\)的度数,再设一个合适正整数\(L\),代表根号分治的临界值。将所有的点分为两个集合\(S_1,S_2\),使得\(\forall u\in S_1,d_u\le L;\forall u\in S_2,d_u>L\)。有一个显然的性质是\(|S_2|\le \big\lfloor \dfrac{2\times n}{L}\big\rfloor\)。
先考虑查询。对于所有\(S_1\)中的点,因其邻居的个数最多是\(L\),所以珂以暴力枚举邻居算\(\operatorname{mex}\)。这个算法是珂以做到\(O(L)\)的(答案输出的\(\operatorname{mex}\)的最大值只能是\(n\),所以你珂以把所有大于\(n\)的数设置成\(n\))。对于所有\(S_2\)中的点,你就必须要对每个\(S_2\)中的点用一个奇技淫巧的数据结构来维护了。这个数据结构必须要支持全局查询\(\operatorname{mex}\)。
先不谈这个数据结构要实现哪些东西,我们来看看修改。修改是单点修改。一个点的修改只会影响到它周边邻居的答案。设点\(u\)的邻居的集合为\(C_u\)。对于集合\(S_1\cap C_u\)中的所有点,珂以不用管它们,因为我们在询问时是暴力枚举计算的。所以我们只要考虑集合\(S_2\cap C_u\)中的所有点,这些点的个数最多也只有\(\big\lfloor \dfrac{2\times n}{L}\big\rfloor\)个。所以我们珂以暴力枚举这样的邻居,然后对于每一个邻居,用维护它的那个数据结构来修改。具体来说就是在这个数据结构中删除原有的数,再插入新添加的数。
所以这个数据结构要支持的就是:1. 插入; 2. 删除; 3. 查询全局\(\operatorname{mex}\)。那么问题来了,这个数据结构是啥呢?显然平衡树啊!每个节点维护两个值:\(val, cnt\)。这样询问时就直接二分,判断ls->size
是否等于this->cnt
即珂。
所以查询时的时间复杂度为\(O(L+\log n)\),修改的时间复杂度为\(O(\big\lfloor \dfrac{2\times n}{L}\big\rfloor \log n)\)。显然,由均值不等式,当\(L\)取\(\sqrt {2\times n\log n}\)时,时间复杂度取到最优值为\(O(n\sqrt{n\log n})\)。(然后窝这个复杂度跑了\(655\)ms,勇获rnk\(2\),吊打了一堆\(O(n\sqrt{n\log n})\)和\(O(n\sqrt{n})\)的,而且还没开Ofast
)。
代码:
#include <bits/stdc++.h>
using namespace std;
constexpr int _I_Buffer_Size = 32 << 20;
static char _I_Buffer[_I_Buffer_Size];
char *_I_pos = _I_Buffer;
void read(int32_t *n) {
while (*_I_pos < 48) _I_pos++;
*n = *_I_pos++ - '0';
while (*_I_pos > 47)
*n = *n * 10 + (*_I_pos++ - '0');
}
constexpr int Maxn = 1e5 + 5, BL = 1024;
int n, m, q;
int a[Maxn];
vector<int> g[Maxn];
int deg[Maxn];
__attribute__((__always_inline__)) __inline
void add_edge(int eu, int ev) {
g[eu].push_back(ev), deg[ev]++;
}
bool appear[Maxn];
namespace treap {
constexpr int Maxs = ::Maxn * 127;
static mt19937 rng(40113143); // 20051212 + 20060617 + 1314
int ls[Maxs], rs[Maxs], fix[Maxs], size[Maxs], tot;
int val[Maxs], cnt[Maxs];
__attribute__((__always_inline__)) __inline
void init_treap() {
tot = 0;
ls[0] = rs[0] = 0;
size[0] = 0, fix[0] = 0;
val[0] = cnt[0] = 0;
}
__attribute__((__always_inline__)) __inline
int newnode(int v) {
++tot;
ls[tot] = rs[tot] = 0;
fix[tot] = rng();
size[tot] = 1;
val[tot] = v;
cnt[tot] = 1;
return tot;
}
__attribute__((__always_inline__)) __inline
void pushup(int p) {
size[p] = size[ls[p]] + size[rs[p]] + 1;
}
int join(int l, int r) {
if (!l || !r) return l | r;
if (fix[l] < fix[r]) {
rs[l] = join(rs[l], r);
pushup(l);
return l;
}
else {
ls[r] = join(l, ls[r]);
pushup(r);
return r;
}
}
void split_s(int p, int s, int &l, int &r) {
if (!p) l = r = 0;
else {
int lsize = size[ls[p]] + 1;
if (lsize <= s) {
l = p;
split_s(rs[p], s - lsize, rs[l], r);
pushup(l);
}
else {
r = p;
split_s(ls[p], s, l, ls[r]);
pushup(r);
}
}
}
void split_v(int p, int v, int &l, int &r) {
if (!p) l = r = 0;
else {
if (val[p] <= v) {
l = p;
split_v(rs[p], v, rs[l], r);
pushup(l);
}
else {
r = p;
split_v(ls[p], v, l, ls[r]);
pushup(r);
}
}
}
void insert(int &p, int x) {
int A, B, C;
split_v(p, x - 1, A, B);
split_v(B, x, B, C);
if (size[B] > 0) cnt[B]++;
else B = newnode(x);
p = join(join(A, B), C);
}
void erase(int &p, int x) {
int A, B, C;
split_v(p, x - 1, A, B);
split_v(B, x, B, C);
if (--cnt[B] == 0) B = 0;
p = join(join(A, B), C);
}
int mex(int p) {
int c = 0;
while (p) {
int lsize = size[ls[p]] + 1;
if (lsize + c == val[p] + 1) {
c += lsize;
p = rs[p];
}
else {
p = ls[p];
}
}
return c;
}
} // namespace treap
using namespace treap;
int root[Maxn];
void re_clear() {
for (int i = 1; i <= n; ++i) {
g[i].clear();
deg[i] = 0;
}
memset(root, 0, (n + 1) << 2);
}
void Main() {
read(&n), read(&m);
for (int i = 1; i <= n; ++i) {
read(a + i);
if (a[i] > n) a[i] = n + 1;
}
for (int i = 1; i <= m; ++i) {
int u, v;
read(&u), read(&v);
add_edge(u, v);
add_edge(v, u);
}
static const auto cmp_node =
[&](int x, int y)->bool {
return deg[x] > deg[y];
};
for (int i = 1; i <= n; ++i)
sort(g[i].begin(), g[i].end(), cmp_node);
init_treap();
for (int u = 1; u <= n; ++u)
if (deg[u] > BL) {
for (int &v: g[u]) treap::insert(root[u], a[v]);
}
read(&q);
while (q--) {
int op, u, x;
read(&op), read(&u);
if (op == 1) {
read(&x);
if (x > n) x = n + 1;
for (int &v: g[u]) {
if (deg[v] <= BL) break;
treap::erase(root[v], a[u]);
treap::insert(root[v], x);
}
a[u] = x;
}
else {
if (deg[u] <= BL) {
for (int &v: g[u])
appear[a[v]] = true;
for (int i = 0; ; ++i)
if (!appear[i]) {
printf("%d\n", i);
break;
}
for (int &v: g[u])
appear[a[v]] = false;
}
else {
printf("%d\n", treap::mex(root[u]));
}
}
}
re_clear();
}
int main() {
fread(_I_Buffer, 1, _I_Buffer_Size, stdin);
int tests;
read(&tests);
while (tests--) Main();
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4414407/blog/4455240