Description
Given an undirected graph
G
=
(
V
,
E
)
G=(V,E)
G=(V,E). All vertices are numbered from
1
1
1 to
N
N
N. And every vertex u has a value of
A
u
A_u
Au. Let
S
u
S_u
Su=
{
A
v
│
(
u
,
v
)
∈
E
}
\left\{ Av│(u,v)∈E\right\}
{Av│(u,v)∈E}. Also,
F
(
u
)
F(u)
F(u) equals MEX(minimum excludant) value of
S
u
S_u
Su. A MEX value of a set is the smallest non-negative integer which doesn’t exist in the set.
There are two types of queries.
Type 1: 1 u x – Change
A
u
A_u
Au to x
(
0
≤
x
≤
1
0
9
)
(0≤x≤10^9)
(0≤x≤109).
Type 2: 2 u – Calculate the value of
F
(
u
)
F(u)
F(u).
For each query of type 2, you should answer the query.
Input
The first line of input contains a single integer
T
(
1
≤
T
≤
10
)
T ~(1≤T≤10)
T (1≤T≤10) denoting the number of test cases. Each test case begins with a single line containing two integers
n
(
1
≤
n
≤
1
0
5
)
n~(1≤n≤10^5)
n (1≤n≤105),
m
(
1
≤
m
≤
1
0
5
)
m~(1≤m≤10^5)
m (1≤m≤105) denoting the number of vertices and number of edges in the given graph.
The next line contains n integers and ith of them is a value of A i ( 0 ≤ A i ≤ 1 0 9 ) A_i (0≤A_i≤10^9) Ai(0≤Ai≤109).
The next m lines contain edges of the graph. Every line contains two integers u, v meaning there exist an edge between vertex u and v.
The next line contains a single integer q ( 1 ≤ q ≤ 1 0 5 ) q~(1≤q≤10^5) q (1≤q≤105) denoting the number of queries.
The next q lines contain queries described in the description.
Output
For each query of type 2, output the value of
F
(
u
)
F(u)
F(u) in a single line.
Examples
Sample Input
1
5 4
0 1 2 0 1
1 2
1 3
2 4
2 5
5
2 2
1 2 2
2 2
1 3 1
2 1
Sample Output
2
2
0
Source
2020 Multi-University Training Contest 1
Main idea
给你一张图,每个点有权值,操作一为修改某一点权值;操作二为询问某一点相邻点点权的MEX
Solution 1
纯分块
注意到一个点的Mex不可能超过其度数,我们按照每个点的度数大小将所有点分为:
“大点” :度数大于
m
\sqrt{m}
m 的点 (其个数不会超过
m
\sqrt{m}
m)
“小点” :度数不大于
m
\sqrt{m}
m的点
将大点的相邻点的权值分块,维护每个块内有多少个不同的数
更新:撤销原值的影响 + 更新新值的影响 维护u相邻的所有大点的块内信息
O
(
m
)
O(\sqrt{m})
O(m)
查询:小点暴力
O
(
m
)
O(\sqrt{m})
O(m);大点也暴力(分块)
O
(
m
)
O(\sqrt{m})
O(m)
分块求MEX : 维护每个块内有多少不同的数,从小到大找块,若块不满,就进去找缺失的最小值
Solution 2
分块 + 树状数组 + 二分
点依然按照 m \sqrt{m} m 分块,只是用树状数组来维护大点相邻点不同权值个数,查询时套上二分
更新:树状数组单点修改
O
(
log
n
)
O(\log{n})
O(logn)
查询:小点暴力
O
(
m
)
O(\sqrt{m})
O(m) ; 大点二分+树状数组区间查询
O
(
(
log
n
)
2
)
O((\log{n})^2)
O((logn)2)
Code 1
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn = 1e5 + 5;
const int maxp = 320;
int n,m,a[maxn];
vector<int>Edge[maxn];
vector<int>mx[maxn];
int lim,tot,d[maxn],id[maxn];
int blksiz[maxn],mxblk[maxn];
vector<int>blk[maxp];//权值所对应的blk编号
vector<int>cnt[maxp];//权值出现次数
vector<int>siz[maxp];//blk内不同权值个数
inline int get_L(int u,int x){return (x-1) * blksiz[id[u]];}
inline int get_R(int u,int x){return x == mxblk[id[u]] ? d[u] - 1 : x * blksiz[id[u]] - 1;}
inline void update(int u,int x){
for(auto v : mx[u]){
if(a[u] < d[v]) if(--cnt[id[v]][a[u]] == 0) siz[id[v]][blk[id[v]][a[u]]]--;
if(x < d[v]) if(++cnt[id[v]][x] == 1) siz[id[v]][blk[id[v]][x]]++;
}
a[u] = x;
}
bool vis[maxp];
inline int query(int u){
//小点暴力
//O(sqrt(n))
if(d[u] <= lim){
for(int i = 0;i < d[u];++i) vis[i] = false;
for(auto v: Edge[u]) if(a[v] < d[u]) vis[a[v]] = true;
for(int i = 0;i < d[u];++i) if(!vis[i]) return i;
return d[u];
} else {
//大点也暴力(分块)
//O(sqrt(n))
for(int i = 1;i <= mxblk[id[u]];++i){
if(siz[id[u]][i] != get_R(u,i) - get_L(u,i) + 1){
int L = get_L(u,i), R = get_R(u,i);
for(int j = L;j <= R;++j)
if(!cnt[id[u]][j]) return j;
break;
}
}
return d[u];
}
}
inline void init(){
tot = 0;
for(int i = 0;i <= n;++i){
Edge[i].clear();mx[i].clear();
d[i] = 0;id[i] = 0;
}
for(int i = 0;i < maxp;++i){
blk[i].clear();cnt[i].clear();siz[i].clear();
blksiz[i] = mxblk[i] = 0;
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);init();
for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
for(int i = 1,u,v;i <= m;++i){
scanf("%d%d",&u,&v);
Edge[u].pb(v);Edge[v].pb(u);
d[u]++, d[v]++;
} lim = sqrt(2.0*m);
for(int i = 1;i <= n;++i){
for(auto v : Edge[i]){
if(d[v] > lim) mx[i].pb(v);
}
if(d[i] <= lim) continue;
id[i] = ++tot;
blksiz[tot] = (int)sqrt((double)(d[i]-1));
blk[tot].assign(d[i] + 3,0);
cnt[tot].assign(d[i] + 3,0);
siz[tot].assign(blksiz[tot] + 3,0);
for(int j = 0;j < d[i];++j) blk[tot][j] = j / blksiz[tot] + 1;
mxblk[tot] = blk[tot][d[i]-1];
for(auto v : Edge[i]){
if(a[v] >= d[i]) continue;
if(++cnt[tot][a[v]] == 1) siz[tot][blk[tot][a[v]]]++;
}
}
int q;scanf("%d",&q);
while(q--){
int op,u;scanf("%d%d",&op,&u);
if(op == 1){
int x;scanf("%d",&x);
update(u,x);
} else if(op == 2) printf("%d\n", query(u));
}
}
return 0;
}
Code 2
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn = 2e5 + 5;
const int maxp = 500;
inline int lowbit(int x){return x &(-x);}
int n,m,a[maxn];
vector<int>bit[maxp];
vector<int>Edge[maxn];
vector<int>cnt[maxp];
vector<int>node[maxn];
int d[maxn];
int tot,lim,id[maxn];
inline void add(int x,int pos,int v){
while(pos < d[x]){
bit[id[x]][pos] += v;pos += lowbit(pos);
}
}
inline int ask(int x,int pos){
int res = 0;
while(pos > 0){
res += bit[id[x]][pos];pos -= lowbit(pos);
} return res;
}
inline void update(int u,int x){
for(auto v : node[u]){
if(a[u] < d[v]) if(--cnt[id[v]][a[u]] == 0 && a[u]) add(v,a[u],-1);
if(x < d[v]) if(++cnt[id[v]][x] == 1 && x) add(v,x,1);
}
a[u] = x;
}
bool vis[maxp];
inline int query(int u){
if(d[u] <= lim){
for(int i = 0;i < d[u];++i) vis[i] = false;
for(auto v : Edge[u]){
if(a[v] < d[u]) vis[a[v]] = true;
}
for(int i = 0;i < d[u];++i){
if(!vis[i]) return i;
}
return d[u];
} else{
if(!cnt[id[u]][0]) return 0;
int l = 1, r = d[u], mex = d[u];
while(l <= r){
int mid = (l + r) >> 1;
if(ask(u,mid) < mid) mex = mid, r = mid - 1;
else l = mid + 1;
}
return mex;
}
}
void init(){
tot = 0;
for(int i = 0;i <= n;++i) Edge[i].clear(), node[i].clear(), d[i] = 0;
for(int i = 0;i < maxp;++i){
cnt[i].clear();bit[i].clear();
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);init();
for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
for(int i = 1,u,v;i <= m;++i){
scanf("%d%d",&u,&v);Edge[u].pb(v);Edge[v].pb(u);
d[u]++, d[v]++;
}
lim = sqrt(m);
for(int i = 1;i <= n;++i){
if(d[i] <= lim) continue;
id[i] = ++tot;
bit[tot].assign(d[i] + 10,0);
cnt[tot].assign(d[i] + 10,0);
for(auto v: Edge[i]){
if(a[v] > d[i]) continue;
cnt[tot][a[v]]++;
if(cnt[tot][a[v]] == 1 && a[v]) add(i,a[v],1);
}
}
for(int i = 1;i <= n;++i){
for(auto v:Edge[i]){
if(d[v] > lim) node[i].pb(v);
}
}
int q;scanf("%d",&q);
while(q--){
int op,u,x;scanf("%d%d",&op,&u);
if(op == 1){
scanf("%d",&x); update(u,x);
} else if(op == 2){
printf("%d\n", query(u));
}
}
}
return 0;
}