• 【2020杭电多校round1 1006】HDU6766 Finding a MEX


    题目大意

    题目链接

    给定一张(n)个点(m)条边的无向图(G=(V,E))。每个点(u)有一个点权(a_u)。令(S_u={a_v|(u,v)in E})。令(F_u=operatorname{mex}(S_u)),其中(operatorname{mex})表示最小的、在集合里没出现过的,非负整数。

    接下来会给出(q)次操作,操作有两种:

    • (1 u x),把点(u)的点权改成(x),也就是令(a_u:=x)
    • (2 u),查询(F_u)的值。

    请你对所有操作(2)输出答案。

    数据范围:(1leq n,mleq 10^5)(0leq a_uleq 10^9)(1leq qleq 10^5)

    本题题解

    设置一个值(B)。按点的度数(>B)(leq B),将点分为“大点”和“小点”。每个小点的邻居数不超过(B),大点的总数不超过(frac{n}{B}),这对我们来说是很好的性质。

    对于一次修改操作,如果被修改的点是小点,则可以直接暴力枚举它的所有邻居,进行修改。如果被修改的点是大点,则什么也不做(有点像线段树的懒标记,先放着,等查询的时候再说)。

    查询时,所有小点对当前点的影响已经维护好了。考虑大点的影响,暴力枚举所有与当前点相邻的大点,如果它被修改过,更新当前点的(F)值即可。因为大点的总数只有(O(frac{n}{B}))个,所以一次操作最多做(O(frac{n}{B}))次修改。

    那么问题就转化为,如何维护出每个点的(S)集合,支持加入一个值、删除一个值、查询(operatorname{mex})。如果用动态开点线段树(给图上每个点都开一棵),则一次修改时间复杂度是(O(log a))的,一次查询时间复杂度是(O(1))的。空间复杂度理论上最大甚至高达(O((n+q)(B+frac{n}{B})log a))。如果(B)(sqrt{n}),则时间、空间复杂度都是(O(nsqrt{n}log a))的。虽然实际上可以AC,但还可以继续优化。

    我们发现,一个点的(F_u)值(也就是(operatorname{mex}(S_u))),不会超过(operatorname{deg}(u))(因为(S_u)的大小不超过(operatorname{deg}(u)))。所以线段树的值域只需要开到(n),时、空复杂度优化为(O(nsqrt{n}log n))

    进一步优化,考虑不用线段树。我们发现,线段树的特点是,修改较慢,查询很快(是(O(1))的)。但是我们恰恰需要较快的修改(因为修改操作需要进行(O((n+q)sqrt{n}))次),而查询则可以慢一点(因为只会做(O(q))次查询)。

    于是考虑用分块替代线段树。对值域分块,用数组记录每个值的出现次数,和每个块内有多少种不同的值,这样修改是(O(1))的。因为每个点值域是(operatorname{deg}(u))的,所以记录这些需要的总空间就是(sum operatorname{deg}(u)=O(m))的。查询时,暴力枚举所有的块,时间复杂度是(O(sqrt{operatorname{deg}(u)})=O(sqrt{n}))的。于是,我们通过分块,牺牲了查询的复杂度,实现了(O(1))修改。

    总时间复杂度降为(O(n+qsqrt{n}))

    参考代码:

    //problem:1006
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
    template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}
    
    const int B=300;
    const int MAXN=1e5;
    
    int n,m,q,a[MAXN+5],deg[MAXN+5];
    bool is_big[MAXN+5];
    vector<int>G[MAXN+5],cnt[MAXN+5],tag[MAXN+5];
    vector<pii>big_nb[MAXN+5];//big neighbour
    
    inline void ins(int u,int x){
    	ckmin(x,deg[u]);
    	cnt[u][x]++;
    	if(cnt[u][x]==1)
    		tag[u][x/B]++;
    }
    inline void del(int u,int x){
    	ckmin(x,deg[u]);
    	cnt[u][x]--;
    	if(cnt[u][x]==0){
    		tag[u][x/B]--;
    	}
    }
    int query_mex(int u){
    	for(int i=0;i<=deg[u];i+=B){
    		int j=min(i+B-1,deg[u]);
    		if(tag[u][i/B] < j-i+1){
    			for(int k=i;k<=j;++k)
    				if(!cnt[u][k])
    					return k;
    			throw;
    		}
    	}
    	throw;
    }
    
    void solve_case(){
    	cin>>n>>m;
    	for(int i=1;i<=n;++i){
    		deg[i]=0;
    		is_big[i]=false;
    		vector<int>().swap(G[i]);
    		vector<int>().swap(cnt[i]);
    		vector<int>().swap(tag[i]);
    		vector<pii>().swap(big_nb[i]);
    		//相当于 v.clear()
    	}
    	for(int i=1;i<=n;++i)
    		cin>>a[i];
    	for(int i=1;i<=m;++i){
    		int u,v; cin>>u>>v;
    		G[u].pb(v); G[v].pb(u);
    		deg[u]++; deg[v]++;
    	}
    	for(int i=1;i<=n;++i){
    		is_big[i]=(deg[i]>B);
    		cnt[i].resize(deg[i]+1);
    		tag[i].resize(deg[i]/B+1);
    	}
    	for(int u=1;u<=n;++u){
    		for(int i=0;i<SZ(G[u]);++i){
    			int v=G[u][i];
    			if(is_big[v])
    				big_nb[u].pb(mk(v,a[v]));
    			ins(u,a[v]);
    		}
    	}
    	cin>>q;
    	for(int tq=1;tq<=q;++tq){
    		int op; cin>>op;
    		if(op==1){
    			int u,new_a; cin>>u>>new_a;
    			if(is_big[u]){
    				a[u]=new_a;
    			}
    			else{
    				for(int i=0;i<SZ(G[u]);++i){
    					int v=G[u][i];
    					del(v,a[u]);
    					ins(v,new_a);
    				}
    				a[u]=new_a;
    			}
    		}
    		else{
    			int u; cin>>u;
    			for(int i=0;i<SZ(big_nb[u]);++i){
    				int v=big_nb[u][i].fi;
    				int old_a=big_nb[u][i].se;
    				if(old_a != a[v]){
    					del(u,old_a);
    					ins(u,a[v]);
    					big_nb[u][i].se=a[v];
    				}
    			}
    			cout<<query_mex(u)<<endl;
    		}
    	}
    }
    int main(){
    	int T;cin>>T;while(T--){
    		solve_case();
    	}
    	return 0;
    }
    
  • 相关阅读:
    页面性能:如何系统地优化页面?
    为什么CSS动画比JavaScript高效?
    Code Review
    浏览器中的页面之CSS是如何影响到渲染流程的
    async / await
    手撸Promise
    Promise
    宏任务和微任务
    有点恶心,随手写点儿
    关于判断用户输入的是不是int类型,这次没有正则表达式
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13357864.html
Copyright © 2020-2023  润新知