题目
题解
方法一、\(\mathcal O(n\sqrt n\log n)\)
考虑到数据范围 \(n\le 100000\),在分块可以接受的范围内,同时,如果我们暴力修改,肯定和点的度有关,那么我们不妨将点的度进行分块:
- 对于度数小于等于 \(\sqrt n\) 的点,修改的时候暴力改,询问的时候暴力问;
- 对于度数大于 \(\sqrt n\) 的点,首先这种点最多 \(\sqrt n\) 个,那么我们用一种数据结构对于这不超过 \(\sqrt n\) 个点进行维护,而这个数据需要支持单点修改和求 \(\text {mex}\),不难想到 \(\tt BIT\);
这个算法,在第一种情况下是 \(\mathcal O(n\sqrt n)\),但是由于处理大点的情况使用了 \(\tt BIT\) 进行维护,所以最终复杂度 \(\mathcal O(n\sqrt n \log n)\),在 \(T\le 10\) 的情况下居然还可以过,这个 \(T\) 怕不是假的......
方法二、\(\mathcal O(n\sqrt n)\)
第一种方法的 \(\log n\) 在于使用了 \(\tt BIT\),我们可以考虑怎么不用 \(\tt BIT\) 对于大点进行维护.
考虑值域分块,每 \(\sqrt n\) 个数分成一块,对于每个块维护这个块中出现了多少不同的数字,询问的时候从小到大枚举块,在第一个不满的块中再暴力找.
这样,修改 \(\mathcal O(q\sqrt n)\)(小块暴力改),询问 \(\mathcal O(q\sqrt n)\)(大块暴力枚举每个块),时间复杂度就变成了 \(\mathcal O(q\sqrt n)\).
# include <cstdio>
# include <set>
# include <algorithm>
# include <vector>
using namespace std;
namespace Elaina{
# define rep(i,l,r) for(int i=l, i##_end_ = r; i <= i##_end_; ++ i)
# define fep(i,l,r) for(int i=l, i##_end_ = r; i >= i##_end_; -- i)
# define fi first
# define se second
# define Endl putchar('\n')
# define writc(x, c) fwrit(x), putchar(c)
// # define int long long
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
typedef unsigned int uint;
template<class T>inline T Max(const T x, const T y){return x < y ? y : x;}
template<class T>inline T Min(const T x, const T y){return x < y ? x : y;}
template<class T>inline T fab(const T x){return x < 0 ? -x : x;}
template<class T>inline void getMax(T& x, const T y){x = Max(x, y);}
template<class T>inline void getMin(T& x, const T y){x = Min(x, y);}
template<class T>T gcd(const T x, const T y){return y ? gcd(y, x % y) : x;}
template<class T>inline T readin(T x){
x=0; int f = 0; char c;
while((c = getchar()) < '0' || '9' < c) if(c == '-') f = 1;
for(x = (c ^ 48); '0' <= (c = getchar()) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T>void fwrit(const T x){
if(x < 0)return putchar('-'), fwrit(-x);
if(x > 9)fwrit(x / 10); putchar(x % 10 ^ 48);
}
}
using namespace Elaina;
const int maxn = 1e5;
const int sqn = 320;
int n, m, q;
int a[maxn + 5];
struct BLOCK{
int bcnt[sqn + 5];
int cnt[maxn + 5];
inline void clear(){
rep(i, 0, n) cnt[i] = 0;
rep(i, 0, sqn) bcnt[i] = 0;
}
inline void inser(const int x){
if(++ cnt[x] == 1) ++ bcnt[x / sqn];
}
inline void delet(const int x){
if(-- cnt[x] == 0) -- bcnt[x / sqn];
}
inline int query(){
int elai = 0;
while(bcnt[elai] == sqn) ++ elai;
int i = elai * sqn;
while(cnt[i]) ++ i;
return i;
}
}block[sqn + 5];
struct edge{int to,nxt;
edge(const int T = 0, const int N = 0) : to(T), nxt(N){}
}e[maxn * 2 + 5];
int tail[maxn + 5], d[maxn + 5], ecnt;
inline void add_edge(const int u, const int v){
++ d[u], ++ d[v];
e[++ ecnt] = edge(v, tail[u]); tail[u] = ecnt;
e[++ ecnt] = edge(u, tail[v]); tail[v] = ecnt;
}
/** @brief record the adjacent big point*/
vector<int>G[maxn + 5];
/** @brief the id of big point*/
int id[maxn + 5];
int idcnt;
inline void init(){
n = readin(1), m = readin(1);
ecnt = idcnt = 0;
rep(i, 1, n) tail[i] = d[i] = id[i] = 0, G[i].clear();
rep(i, 1, n) a[i] = Min(readin(1), n);
int u, v;
rep(i, 1, m){
u = readin(1), v = readin(1);
add_edge(u, v);
}
rep(u, 1, n) if(d[u] > sqn){
id[u] = ++ idcnt;
block[id[u]].clear();
for(int i = tail[u]; i; i = e[i].nxt){
G[e[i].to].push_back(u);
block[id[u]].inser(a[e[i].to]);
}
}
}
int bucket[sqn + 5];
signed main(){
int T = readin(1);
while(T --){
init();
int opt, x, val;
q = readin(1);
while(q --){
opt = readin(1), x = readin(1);
/** @brief the option to modify*/
if(opt == 1){
val = Min(readin(1), n);
/** @brief just need to modify the adjacent bit point*/
for(int i = 0, sz = G[x].size(); i < sz; ++ i){
block[id[G[x][i]]].delet(a[x]);
block[id[G[x][i]]].inser(val);
}
a[x] = val;
} else{
/** @brief if this is a big point*/
if(id[x]) writc(block[id[x]].query(), '\n');
else{
rep(i, 0, sqn + 1) bucket[i] = 0;
for(int i = tail[x]; i; i = e[i].nxt)
++ bucket[Min(a[e[i].to], sqn + 1)];
rep(i, 0, sqn + 1) if(!bucket[i]){
writc(i, '\n'); break;
}
}
}
}
}
return 0;
}