zoukankan      html  css  js  c++  java
  • [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,'
    ');
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    BZOJ3832: [Poi2014]Rally(拓扑排序 堆)
    UVAlive6807 Túnel de Rata (最小生成树)
    UVAlive6800The Mountain of Gold?(负环)
    cf623A. Graph and String(二分图 构造)
    BZOJ4144: [AMPPZ2014]Petrol(最短路 最小生成树)
    cf605D. Board Game(BFS 树状数组 set)
    为什么要去创业?
    后缀数组练习题若干
    Android开发 之 我的jar包引用方法
    IBM-ETP 实训项目前一天
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14224056.html
Copyright © 2011-2022 走看看