题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=6756
题目大意
给定 N 个带值点和 M 条边 , 其中第 i 个点的值为 ai
有 Q 次操作 , 每次操作有以下两种类型 :
①、将第 u 个点的权值修改为 x
②、查询与第 u 个点的相邻点集的 MEX
解题思路
经典根号分治
考虑把修改的复杂度提高以便降低查询的复杂度
先将度数大于等于根号 N 的点设为重点 , 度数小于根号 N 的点设为轻点
因为 M <= 1e5 , 所以重点数不超过 350 , 那么就可以对每个重点建立一个权值数组
①、对于轻点的查询直接暴力枚举与之相邻点的点集的MEX( 最多不超过 sqrt(n) 个)
②、对于重点的查询在树状数组上二分跑答案即可 ( 判断前缀权值和是否等于 mid )
③、对节点 u 的修改只要修改与其相邻重点的权值树状数组的权值 and a[u] 的值
时间复杂度为 $O\left( q\left( \sqrt{n}+\log n^{2}\right) \right) $
为了防止爆空间 , bit 得使用 vector 动态建立( 注意 vector 开辟空间后要初始化 )
AC_Code
#include<bits/stdc++.h> #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long using namespace std; const int N = 3e5 + 10 ; struct Edge{ int nex , to; }edge[N << 1]; int head[N] , TOT; void add_edge(int u , int v) { edge[++ TOT].nex = head[u] ; edge[TOT].to = v; head[u] = TOT; } vector<int>tree[N] , cnt[N] , vec[N]; int n , m , q , sq; int a[N] , du[N] , zero[N] , vis[N]; int lowbit(int x) { return x & (-x); } void add(int pos , int x , int id) { int up = du[id] + 5; while(pos < up) { tree[id][pos] += x; pos += lowbit(pos); } } int get_sum(int pos , int id) { int res = 0; while(pos) { res += tree[id][pos]; pos -= lowbit(pos); } return res; } void init(int n) { TOT = 0; rep(i , 1 , n) zero[i] = head[i] = du[i] = 0 , vec[i].clear(); } void change(int u , int x) { if(!a[u]) for(auto i : vec[u]) zero[i] -- ; else { for(auto i : vec[u]) { if(a[u] > du[i]) continue ; cnt[i][a[u]] -- ; if(!cnt[i][a[u]]) add(a[u] , -1 , i); } } if(!x) { for(auto i : vec[u]) zero[i] ++ ; a[u] = x; return ; } for(auto i : vec[u]) { if(x > du[i]) continue ; cnt[i][x] ++ ; if(cnt[i][x] == 1) add(x , 1 , i); } a[u] = x; } int query1(int u) { rep(i , 0 , du[u]) vis[i] = 0; for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to ; if(a[v] > du[u]) continue ; vis[a[v]] ++ ; } rep(i , 0 , 330) if(!vis[i]) return i; } int query2(int u) { if(!zero[u]) return 0; int l = 1 , r = du[u] , res = du[u]; while(l <= r) { int mid = l + r >> 1; if(get_sum(mid , u) < mid) r = mid - 1 , res = mid; else l = mid + 1; } return res; } signed main() { ios::sync_with_stdio(false); cin.tie(0) , cout.tie(0); int t ; cin >> t; while(t --) { cin >> n >> m ; sq = sqrt(n); rep(i , 1 , n) cin >> a[i]; rep(i , 1 , m) { int u , v; cin >> u >> v; add_edge(u , v) , add_edge(v , u); du[u] ++ , du[v] ++ ; } rep(u , 1 , n) { for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to; if(du[v] >= sq) vec[u].push_back(v); } } rep(u , 1 , n) { if(du[u] < sq) continue ; tree[u].resize(du[u] + 10); cnt[u].resize(du[u] + 10); rep(j , 0 , du[u]) tree[u][j] = cnt[u][j] = 0; for(int i = head[u] ; i ; i = edge[i].nex) { int v = edge[i].to ; if(a[v] > du[u]) continue ; if(!a[v]) {zero[u] ++ ; continue ;} cnt[u][a[v]] ++ ; if(cnt[u][a[v]] == 1) add(a[v] , 1 , u); } } cin >> q; while(q --) { int op , u , x; cin >> op; if(op == 1) { cin >> u >> x ; change(u , x); } else { cin >> u; if(du[u] <= sq) cout << query1(u) << '\n'; else cout << query2(u) << '\n'; } } init(n); } return 0; }