2020杭电多校第一场 hdu6756 Finding a MEX

题目链接

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;
}
上一篇:Linux磁盘管理


下一篇:CentOS 清理空间