[HDU6756] Finding a MEX

前言

我爱任何形式的暴力

这道题解法是我比较薄弱但喜欢的分块

这是我写这篇博客的原因

而且这道题让我心态崩掉,直接重构代码,总用时2h

理清思路并不难写(第一次尝试写代码之前写一份类似于大纲的东西

感觉不错,下次复杂的题也这么做

题目

HDU

题目大意:

多组输入

给定 \(n\) 个点 \(m\) 条边的一个无向图,每个点 \(i\) 有个权值 \(a_i\)

\(q\) 个操作

操作\(1\)为将第 \(u\) 号点的权值改为 \(x\)

操作\(2\)为询问与 \(u\) 相邻(不包含自己)的点的权值的\(MEX\)

\(MEX\)为最小的未出现过的自然数

讲解

为我们表述方便,我们将度数小于\(\sqrt{n}\)的点叫做小点,其余叫做大点

前言已经提到过,这道题分块

查询

对于小点,我们直接暴力查找即可

对于大点,我们考虑将权值分块,对于每个块我们维护有多少元素确实,对于每个大点维护每个元素出现了多少次

显然大点的个数很少,不超过 \(\sqrt{n}\) 级别

我们只需从小到大访问询问的大点的,找出有缺失的块,然后在其中暴力查找即可

其中有一个优化,因为 \(MEX\) 只可能达到度数级别,所以点的权值如果超过了度数,我们可将其赋为度数-1

修改

修改就不分大点、小点了,对于每个要修改的点,我们要更新它所在的每个块的信息

一个点最多能被多少个大点包含呢?显然也是 \(\sqrt{n}\) 级别,暴力修改即可

代码

许久未见的完整版代码

//12252024832524
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std; 

typedef long long LL;
const int MAXN = 100005;
const int MAXB = 318;
int n,m,ddd; 
int a[MAXN];

int Read()
{
    int x = 0,f = 1;char c = getchar();
    while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    return x * f;
}
void Put1(int x)
{
    if(x > 9) Put1(x/10);
    putchar(x%10^48);
}
void Put(int x,char c = -1)
{
    if(x < 0) putchar('-'),x = -x;
    Put1(x);
    if(c >= 0) putchar(c);
}
template <typename T>T Max(T x,T y){return x > y ? x : y;}
template <typename T>T Min(T x,T y){return x < y ? x : y;}
template <typename T>T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],tot,d[MAXN];//d:度数
struct edge
{
    int v,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y)
{
    e[++tot].v = y;
    e[tot].nxt = head[x];
    head[x] = tot;
}
void Add_Double_Edge(int x,int y)
{
    Add_Edge(x,y); d[y]++;
    Add_Edge(y,x); d[x]++;
}
int ID[MAXN],rID[MAXN],siz[MAXN],btot;//ID,rID:点号与大点编号的互相映射,siz:大点分块大小
int cnt[MAXB][MAXN],cha[MAXB][MAXB];//cnt:每个大点的元素计数器,cha:每个大点的每个块差多少元素填满
int cfa[MAXN];//cnt for ans(奇怪的英语增加了
vector<int> bl[MAXN];//每个点被哪些大点包含

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    for(int T = Read(); T ;-- T)
    {
        for(int i = n;i >= 1;-- i)
            if(d[i] >= ddd)
            {
                for(int j = head[i]; j ;j = e[j].nxt)
                    cnt[btot][Min(a[e[j].v],d[i]-1)] = 0;
                btot--;
            }
       	//初始化确实烦人
        n = Read(); m = Read();
        btot = tot = 0;
        for(int i = 1;i <= n;++ i) ID[i] = head[i] = d[i] = 0,bl[i].clear();
        memset(cha,0,sizeof(cha));
        for(int i = 1;i <= n;++ i) a[i] = Read();
        for(int i = 1;i <= m;++ i) Add_Double_Edge(Read(),Read());
        ddd = ceil(sqrt(n));
        for(int i = 1;i <= n;++ i)
            if(d[i] >= ddd)//大大大 
            {
                ID[i] = ++btot;
                rID[btot] = i;
                siz[i] = ceil(sqrt(d[i]));//每个大点分为sqrt(d[i])个块
                for(int j = 0;j <= siz[i];++ j) cha[btot][j] = siz[i];
                for(int j = head[i]; j ;j = e[j].nxt)
                {
                    int val = Min(a[e[j].v],d[i]-1);
                    bl[e[j].v].push_back(btot);//e[j].v被大点btot包含
                    cnt[btot][val]++;
                    if(cnt[btot][val] == 1) cha[btot][val / siz[i]]--;
                }
            }
        for(int Q = Read(); Q ;-- Q)
        {
            int opt = Read(),x = Read();
            if(opt == 1)
            {
                int val = Read();
                for(int j = 0,len = bl[x].size();j < len;++ j)
                {
                    //删除以前的信息
                    int bid = bl[x][j],jval = Min(a[x],d[rID[bid]]-1);
                    cnt[bid][jval]--;
                    if(!cnt[bid][jval]) cha[bid][jval / siz[rID[bid]]]++;
                    //更新现在的信息
                    jval = Min(val,d[rID[bid]]-1);
                    cnt[bid][jval]++;
                    if(cnt[bid][jval] == 1) cha[bid][jval / siz[rID[bid]]]--;
                }
                a[x] = val;
            }
            else
            {
                int ans = n;
                if(d[x] < ddd)//小点暴力找
                {
                    for(int i = head[x]; i ;i = e[i].nxt) cfa[Min(n,a[e[i].v])]++;
                    for(int i = 0;i <= n && ans == n;++ i)
                        if(!cfa[i]) ans = i;
                    for(int i = head[x]; i ;i = e[i].nxt) cfa[Min(n,a[e[i].v])]--;
                }
                else//大点分块
                {
                    int bid = ID[x];
                    for(int i = 0;i <= siz[x] && ans == n;++ i)//siz[x]个块
                        if(cha[bid][i])//该块有空缺
                            for(int j = i * siz[x];j < (i+1) * siz[x] && ans == n;++ j)//每个块有siz[x]个元素
                                if(!cnt[bid][j])
                                    ans = j;
                }
                Put(ans,'\n');
            }
        }
    }
    return 0;
}
posted @ 2021-01-02 21:40  皮皮刘  阅读(91)  评论(0编辑  收藏  举报