题目链接
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;
}