[HDU-6756]Finding a MEX

题目

传送门

题解

方法一、\(\mathcal O(n\sqrt n\log n)\)

考虑到数据范围 \(n\le 100000\),在分块可以接受的范围内,同时,如果我们暴力修改,肯定和点的度有关,那么我们不妨将点的度进行分块:

  • 对于度数小于等于 \(\sqrt n\) 的点,修改的时候暴力改,询问的时候暴力问;
  • 对于度数大于 \(\sqrt n\) 的点,首先这种点最多 \(\sqrt n\) 个,那么我们用一种数据结构对于这不超过 \(\sqrt n\) 个点进行维护,而这个数据需要支持单点修改和求 \(\text {mex}\),不难想到 \(\tt BIT\)

这个算法,在第一种情况下是 \(\mathcal O(n\sqrt n)\),但是由于处理大点的情况使用了 \(\tt BIT\) 进行维护,所以最终复杂度 \(\mathcal O(n\sqrt n \log n)\),在 \(T\le 10\) 的情况下居然还可以过,这个 \(T\) 怕不是假的......

方法二、\(\mathcal O(n\sqrt n)\)

第一种方法的 \(\log n\) 在于使用了 \(\tt BIT\),我们可以考虑怎么不用 \(\tt BIT\) 对于大点进行维护.

考虑值域分块,每 \(\sqrt n\) 个数分成一块,对于每个块维护这个块中出现了多少不同的数字,询问的时候从小到大枚举块,在第一个不满的块中再暴力找.

这样,修改 \(\mathcal O(q\sqrt n)\)(小块暴力改),询问 \(\mathcal O(q\sqrt n)\)(大块暴力枚举每个块),时间复杂度就变成了 \(\mathcal O(q\sqrt n)\).

# include <cstdio>
# include <set>
# include <algorithm>
# include <vector>
using namespace std;
namespace Elaina{
    # define rep(i,l,r) for(int i=l, i##_end_ = r; i <= i##_end_; ++ i)
    # define fep(i,l,r) for(int i=l, i##_end_ = r; i >= i##_end_; -- i)
    # define fi first
    # define se second
    # define Endl putchar('\n')
    # define writc(x, c) fwrit(x), putchar(c)
    // # define int long long
    typedef long long ll;
    typedef pair<int, int> pii;
    typedef unsigned long long ull;
    typedef unsigned int uint;
    template<class T>inline T Max(const T x, const T y){return x < y ? y : x;}
    template<class T>inline T Min(const T x, const T y){return x < y ? x : y;}
    template<class T>inline T fab(const T x){return x < 0 ? -x : x;}
    template<class T>inline void getMax(T& x, const T y){x = Max(x, y);}
    template<class T>inline void getMin(T& x, const T y){x = Min(x, y);}
    template<class T>T gcd(const T x, const T y){return y ? gcd(y, x % y) : x;}
    template<class T>inline T readin(T x){
        x=0; int f = 0; char c;
        while((c = getchar()) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c ^ 48); '0' <= (c = getchar()) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        return f ? -x : x;
    }
    template<class T>void fwrit(const T x){
        if(x < 0)return putchar('-'), fwrit(-x);
        if(x > 9)fwrit(x / 10); putchar(x % 10 ^ 48);
    }
}
using namespace Elaina;

const int maxn = 1e5;
const int sqn = 320;


int n, m, q;
int a[maxn + 5];

struct BLOCK{
    int bcnt[sqn + 5];
    int cnt[maxn + 5];
    inline void clear(){
        rep(i, 0, n) cnt[i] = 0;
        rep(i, 0, sqn) bcnt[i] = 0;
    }
    inline void inser(const int x){
        if(++ cnt[x] == 1) ++ bcnt[x / sqn];
    }
    inline void delet(const int x){
        if(-- cnt[x] == 0) -- bcnt[x / sqn];
    }
    inline int query(){
        int elai = 0;
        while(bcnt[elai] == sqn) ++ elai;
        int i = elai * sqn;
        while(cnt[i]) ++ i;
        return i;
    }
}block[sqn + 5];

struct edge{int to,nxt;
    edge(const int T = 0, const int N = 0) : to(T), nxt(N){}
}e[maxn * 2 + 5];
int tail[maxn + 5], d[maxn + 5], ecnt;
inline void add_edge(const int u, const int v){
    ++ d[u], ++ d[v];
    e[++ ecnt] = edge(v, tail[u]); tail[u] = ecnt;
    e[++ ecnt] = edge(u, tail[v]); tail[v] = ecnt;
}
/** @brief record the adjacent big point*/
vector<int>G[maxn + 5];
/** @brief the id of big point*/
int id[maxn + 5];
int idcnt;

inline void init(){
    n = readin(1), m = readin(1);
    ecnt = idcnt = 0;
    rep(i, 1, n) tail[i] = d[i] = id[i] = 0, G[i].clear();
    rep(i, 1, n) a[i] = Min(readin(1), n);
    int u, v;
    rep(i, 1, m){
        u = readin(1), v = readin(1);
        add_edge(u, v);
    }
    rep(u, 1, n) if(d[u] > sqn){
        id[u] = ++ idcnt;
        block[id[u]].clear();
        for(int i = tail[u]; i; i = e[i].nxt){
            G[e[i].to].push_back(u);
            block[id[u]].inser(a[e[i].to]);
        }
    }
}

int bucket[sqn + 5];

signed main(){
    int T = readin(1);
    while(T --){
        init();
        int opt, x, val;
        q = readin(1);
        while(q --){
            opt = readin(1), x = readin(1);
            /** @brief the option to modify*/
            if(opt == 1){
                val = Min(readin(1), n);
                /** @brief just need to modify the adjacent big point*/
                for(int i = 0, sz = G[x].size(); i < sz; ++ i){
                    block[id[G[x][i]]].delet(a[x]);
                    block[id[G[x][i]]].inser(val);
                }
                a[x] = val;
            } else{
                /** @brief if this is a big point*/
                if(id[x]) writc(block[id[x]].query(), '\n');
                else{
                    rep(i, 0, sqn + 1) bucket[i] = 0;
                    for(int i = tail[x]; i; i = e[i].nxt)
                        ++ bucket[Min(a[e[i].to], sqn + 1)];
                    rep(i, 0, sqn + 1) if(!bucket[i]){
                        writc(i, '\n'); break;
                    }
                }
            }
        }
    }
    return 0;
}
posted @ 2021-01-02 17:55  Arextre  阅读(69)  评论(0编辑  收藏  举报