Finding a MEX 分块 + 树状数组

传送门

题目描述

给你一个无向图,每个点都有一个权值 a i a_{i} ai,现在有两种操作
1:将点 x x x的权值改为 w w w
2:将与点 x x x相连的所有点的的权值构成一个集合,求这个集合的 m e x mex mex

分析

这个题的思路比较巧妙
首先我们求 m e x mex mex有很多方法,可以直接暴力,或者树状数组二分,线段树等,但不管任何方法,要么时间复杂度不理想,要么空间复杂度不理想,那么我们就要来权衡一下
首先这个图的所有点的度数最大为 n ∗ ( n − 1 ) n * (n - 1) n(n1),为完全图的情况下,那么这张图中点的度数大于 n \sqrt{n} n 的点必然不会超过 n \sqrt{n} n 个,所以我们可以考虑,对于点的度数大于 n \sqrt{n} n 的点,可以用树状数组进行二分,对于点的度数小于 n \sqrt{n} n 的点,可以直接暴力
时间复杂度为 O ( n n ) O(n \sqrt{n}) O(nn )

代码

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define debug(x) cout<<#x<<":"<<x<<endl;
#define dl(x) printf("%lld\n",x);
#define di(x) printf("%d\n",x);
#define _CRT_SECURE_NO_WARNINGS
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef vector<int> VI;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const ll mod= 1000000007;
const double eps = 1e-9;
const double PI = acos(-1);
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;}
int gcd(int a,int b){return (b>0)?gcd(b,a%b):a;}

int a[N];
VI tr[N];
VI nums[N],G[N];
VI son[N];
bool st[N];
int sz[N];
int id[N];
int d[N];
int n,m;

int lowbit(int x){
    return x & -x;
}

void add(int u,int x,int c){
    for(int i = x;i <= n;i += lowbit(i)) tr[u][i] += c;
}

int sum(int u,int x){
    int res = 0;
    for(int i = x;i;i -= lowbit(i)) res += tr[u][i];
    return res;
}

void cale(int u,int w){
    for(auto v:son[u]){
        int p = id[v];
        if(a[u] <= d[v]){
            nums[p][a[u]]--;
            if(!nums[p][a[u]] && a[u]) add(p,a[u],-1);
        }
        if(w <= d[v]){
            nums[p][w]++;
            if(nums[p][w] == 1 && w) add(p,w,1);
        }
    }
    a[u] = w;
}

int query_max(int u){
    int p = id[u];
    if(!nums[p][0]) return 0;
    int l = 0,r = d[u];
    int mex = sz[p];
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (sum(p, mid) < mid)
            mex = mid, r = mid - 1;
        else
            l = mid + 1;
    }
    return mex;
}

int query_min(int u){
    for(int i = 0;i <= d[u];i++) st[i] = false;
    for(int v:G[u]) st[a[v]] = 1;
    for(int i = 0;i <= d[u];i++) if(!st[i]) return i;
}

int main(){
    int T;
    read(T);
    while(T--){
        read(n),read(m);
        int idx = 0;
        for(int i = 1;i <= n;i++) {
            read(a[i]),id[i] = 0;
            G[i].clear();
            son[i].clear();
        }
        while(m--){
            int x,y;
            read(x),read(y);
            G[x].pb(y),G[y].pb(x);
            d[x]++,d[y]++;
        }
        int limit = sqrt(n);
        for(int i = 1;i <= n;i++){
            if(d[i] < limit) continue;
            id[i] = ++idx;
            nums[idx].resize(d[i] + 5);
            tr[idx].resize(d[i] + 5);
            sz[idx] = d[i];
            for(int j = 0;j <= d[i];j++) nums[idx][j] = tr[idx][j] = 0;
            for(int j:G[i]){
                if(a[j] > d[i]) continue;
                nums[idx][a[j]]++;
                if(nums[idx][a[j]] == 1 && a[j]) add(idx,a[j],1);
            }
        }
        for(int i = 1;i <= n;i++)
            for(int j:G[i])
                if(d[j] >= limit)
                    son[i].pb(j);
        read(m);
        while(m--){
            int op,x,y;
            read(op),read(x);
            if(op == 1){
                read(y);
                cale(x,y);
            }
            else{
                if(d[x] < limit) printf("%d\n",query_min(x));
                else printf("%d\n",query_max(x));
            }
        }
    }
    return 0;
}

/**
*  ┏┓   ┏┓+ +
* ┏┛┻━━━┛┻┓ + +
* ┃       ┃
* ┃   ━   ┃ ++ + + +
*  ████━████+
*  ◥██◤ ◥██◤ +
* ┃   ┻   ┃
* ┃       ┃ + +
* ┗━┓   ┏━┛
*   ┃   ┃ + + + +Code is far away from  
*   ┃   ┃ + bug with the animal protecting
*   ┃    ┗━━━┓ 神兽保佑,代码无bug 
*   ┃        ┣┓
*    ┃        ┏┛
*     ┗┓┓┏━┳┓┏┛ + + + +
*    ┃┫┫ ┃┫┫
*    ┗┻┛ ┗┻┛+ + + +
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值