*Finding a MEX【轻重点分块】-2020杭电多校1

题意:

给出一个图,(n) 个点 (m) 条边,每个点有一个权值 (A_i),定义 (F(u))(mex{A_v|(u,v)in E })。有两种操作:
1.将 (A_u) 的值改为 (x)
2.求出 (F(u)) 的值;

分析:

首先要明确点 (F(u)) 的值一定不会超过点 (u) 的度。选定一个值为度的界限,当点的度超过该值时,视为重点,否则为轻点。对于重点,由于其相连的点的个数比较多,所以用一个 (set) 维护,保证 (set) 的第一个数就是答案;对于轻点,由于相连的点比较少,可以采用直接暴力的方式。通过这种方法,可以降低复杂度。
注意要记录每个数的出现的次数,界限的选取和边的数量有关,此处选 (1000)

代码:

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e5+5;
int a[N],degree[N];//点权和每个点的度
vector<int>pic[N];
int block=1000;
set<int>st[N];
int vis[1010],num[110][N],id[N];//只存重点周围的权值出现的次数
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,u,v,x,q,op;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            pic[i].clear();
            degree[i]=0;
            id[i]=0;
        }
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            pic[u].pb(v);
            pic[v].pb(u);
            degree[u]++,degree[v]++;//记下每个点的度
        }
        //要记录权值出现的次数
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(degree[i]>=block)//重点
            {
                id[i]=++cnt;//记该点是第几个重点
                st[cnt].clear();//保证第一个元素为答案
                for(int j=0;j<=degree[i];j++)
                {
                    st[cnt].insert(j);
                    num[cnt][j]=0;
                }
                for(int j=0;j<pic[i].size();j++)
                {
                    int t=pic[i][j];
                    if(a[t]<=degree[i])//答案不会超过degree[i]
                    {
                        num[cnt][a[t]]++;
                        st[cnt].erase(a[t]);
                    }
                }
            }
        }
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&u,&x);
                for(int i=0;i<pic[u].size();i++)
                {
                    int t=pic[u][i];
                    if(id[t]==0) continue;//不对轻点进行处理
                    if(degree[t]>=block)
                    {
                        if(x<=degree[t])
                        {
                            num[id[t]][x]++;
                            st[id[t]].erase(x);
                        }
                        if(a[u]<=degree[t])
                        {
                            num[id[t]][a[u]]--;
                            if(num[id[t]][a[u]]==0)
                                st[id[t]].insert(a[u]);
                        }
                    }
                }
                a[u]=x;
            }
            else
            {
                scanf("%d",&u);
                int ans=0;
                if(degree[u]<block)//轻点,暴力寻找
                {
                    for(int i=0;i<pic[u].size();i++)
                    {
                        int t=pic[u][i];
                        if(a[t]<=degree[u])
                            vis[a[t]]=1;
                    }
                    for(int i=0;i<=degree[u];i++)
                    {
                        if(!vis[i])
                        {
                            ans=i;
                            break;
                        }
                    }
                    for(int i=0;i<pic[u].size();i++)
                    {
                        int t=pic[u][i];
                        if(a[t]<=degree[u])
                            vis[a[t]]=0;
                    }
                }
                else
                    ans=*st[id[u]].begin();
                printf("%d
",ans);
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/1024-xzx/p/13399716.html