zoukankan      html  css  js  c++  java
  • 2020杭电HDU-6756多校第一场Finding a MEX(图的分块)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6756
    CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107590312

    Problem Description

    Given an undirected graph G=(V,E). All vertices are numbered from 1 to N. And every vertex u has a value of Au. Let Su={Av│(u,v)∈E}. Also, F(u) equals MEX(minimum excludant) value of Su. A MEX value of a set is the smallest non-negative integer which doesn’t exist in the set.

    There are two types of queries.

    Type 1: 1 u x – Change Au to x (0≤x≤10^9).
    Type 2: 2 u – Calculate the value of F(u).

    For each query of type 2, you should answer the query.

    Input
    The first line of input contains a single integer T (1≤T≤10) denoting the number of test cases. Each test case begins with a single line containing two integers n (1≤n≤10^ 5), m (1≤m≤10^5) denoting the number of vertices and number of edges in the given graph.

    The next line contains n integers and ith of them is a value of Ai (0≤Ai≤10^9).

    The next m lines contain edges of the graph. Every line contains two integers u, v meaning there exist an edge between vertex u and v.

    The next line contains a single integer q (1≤q≤10^5) denoting the number of queries.

    The next q lines contain queries described in the description.

    Output
    For each query of type 2, output the value of F(u) in a single line.

    Sample Input
    1
    5 4
    0 1 2 0 1
    1 2
    1 3
    2 4
    2 5
    5
    2 2
    1 2 2
    2 2
    1 3 1
    2 1

    Sample Output
    2
    2
    0

    题目大意:给你一个n个点,m条边的图,每个点有点权,定义(f(u)) 表示与点(u)相连的点不存在的最小权值,现在有两种操作:
    1.将点(u)的值改变为(x)
    2.查询(F(u))

    emmm,刚开始读错了题目,以为是将所有为(A_u)的值都改变为(x),然后怎么想都觉得搞不定QAQ。。

    事实上分块的题目我倒是做过不少,但这种图的分块还真没怎么接触过QAQ。。。

    emmm,先建图吧,然后我们定义大点为度数大于(sqrt{n})的点,然后对于每个点我们建立一个邻居值集,并对这个集合进行分块处理,如下所示:

    void insert(int u,int val)
    {
    	if (val>du[u]) val=du[u]+1;//u的分支就du[u]个,如果邻居的值大于du[u]
    	//说明我们一定可以在du[u]以内个数中找到一个不存在的最小值。
    	//所以对于这样的邻居值是没有意义的,稍微记录一下就好了
    	cnt[u][val]++;
    	if (cnt[u][val]==1) tag[u][val/block_size]++;//值域分块用于快速查找不存在的值
    }
    

    修改的时候,对于小的点,我们直接暴力修改就好了,也就是遍历它的所有邻居,然后从该邻居的值集中删掉该点的旧值,并加入该点的新值,如下所示:

    if (tp==1) {
    	scanf ("%d",&x);
    	if (du[u]>block_size) a[u]=x;
    	else {
    		for (int i=head[u]; i!=-1; i=eg[i].next) {
    			int v=eg[i].to;
    			delet(v,a[u]);//u被改变之后,它的邻居的值集也会变化
    			insert(v,x);
    		}
    		a[u]=x;
    	}
    }
    

    可以看到,对于大点,我们没有暴力修改,因为它所需要的时间会非常久。我们只是将(A_u)进行了修改,那么我们如何查询与(u)相邻的点的(MEX)值呢?所以我们再引入一个东西来保存每个大点的旧值,而这个旧值也就只会在询问的时候发生改变。我们记录每个点的邻居是大点的情况,并将其点和点权放入,询问与大点相邻的点的(MEX)值的时候我们就可以只更改该点的邻居值集,如下所示:

    for (int i=1; i<=n; i++) {
    	for (int j=head[i]; j!=-1; j=eg[j].next) {
    		int v=eg[j].to;
    		if (du[v]>block_size)
    			big_ngb[i].push_back(make_pair(v,a[v]));//邻居是大点的情况
    		insert(i,a[v]);//记录邻居值的出现次数
    	}
    }
    for (int i=0; i<big_ngb[u].size(); i++) {
    	//遍历邻居中的大点,因为大点还没有被改变,所以我们要将他改变
    	int v=big_ngb[u][i].first;
    	int pre_a=big_ngb[u][i].second;//大点的旧值
    	if (pre_a!=a[v]) {
    		delet(u,pre_a);
    		insert(u,a[v]);
    		big_ngb[u][i].second=a[v];
    	}
    }
    printf("%d
    ",query(u));
    

    那么query也就很好写了,就是个普通的分块,每次查看块是否被填满了,如果没有的话就说明答案一定在里面。

    以下是AC代码:(值得一提的是,我加了读入挂和输出挂跑的居然比scanf还慢QAQ,但基本都在1400ms+)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=1e5+10;
    const int inf=1e9+10;
    
    struct Edge
    {
    	int to,next;
    }eg[mac<<1];
    int head[mac],num=0;
    int a[mac],block_size=0,block_num=0;
    int du[mac];
    vector<int>tag[mac],cnt[mac];
    vector<pair<int,int>>big_ngb[mac];//每个点的大点邻居
    
    void add(int u,int v)
    {
    	eg[++num]=Edge{v,head[u]};
    	head[u]=num;
    }
    
    void insert(int u,int val)
    {
    	if (val>du[u]) val=du[u]+1;//u的分支就du[u]个,如果邻居的值大于du[u]
    	//说明我们一定可以在du[u]以内个数中找到一个不存在的最小值。
    	//所以对于这样的邻居值是没有意义的,稍微记录一下就好了
    	cnt[u][val]++;
    	if (cnt[u][val]==1) tag[u][val/block_size]++;//值域分块用于快速查找不存在的值
    }
    
    void delet(int u,int val)
    {
    	if (val>du[u]) val=du[u]+1;
    	cnt[u][val]--;
    	if (cnt[u][val]==0) tag[u][val/block_size]--;
    }
    
    int query(int u)
    {
    	for (int i=0; i<=du[u]; i++){//总共du[u]+1个值,而有du[u]个邻居,那么答案一定在其中
    		int gkd=tag[u][i/block_size];
    		if (gkd==block_size) {i+=block_size-1;  continue;}//这个块被填满了,说明答案不在其中
    		for (int j=i; j<i+block_size; j++){
    			if (!cnt[u][j]) 
    				return j;
    		}
    	}
    }
    
    void init(int n)
    {
    	memset(head,-1,sizeof head);
    	num=0;
    	memset(du,0,sizeof du);
    	for (int i=0; i<=n; i++){
    		big_ngb[i].clear();
    		cnt[i].clear();  tag[i].clear();
    	}
    }
    
    int main(int argc, char const *argv[])
    {
    	int t;
    	scanf ("%d",&t);
    	while (t--){
    		int n,m;
    		scanf ("%d%d",&n,&m);
    		block_size=sqrt(n);
    		init(n);
    		for (int i=1; i<=n; i++)
    			scanf ("%d",&a[i]);
    		for (int i=1; i<=m; i++){
    			int u,v;
    			scanf ("%d%d",&u,&v);
    			add(u,v);add(v,u);
    			du[u]++,du[v]++;
    		}
    		for (int i=1; i<=n; i++){
    			cnt[i].resize(du[i]+2);
    			tag[i].resize(du[i]/block_size+2);//块的个数
    		}
    		for (int i=1; i<=n; i++){
    			for (int j=head[i]; j!=-1; j=eg[j].next){
    				int v=eg[j].to;
    				if (du[v]>block_size) 
    					big_ngb[i].push_back(make_pair(v,a[v]));//邻居是大点的情况
    				insert(i,a[v]);//记录邻居值的出现次数
    			}
    		}
    		int q;
    		scanf ("%d",&q);
    		while (q--){
    			int tp,u,x;
    			scanf ("%d%d",&tp,&u);
    			if (tp==1){
    				scanf ("%d",&x);
    				if (du[u]>block_size) a[u]=x;
    				else {
    					for (int i=head[u]; i!=-1; i=eg[i].next){
    						int v=eg[i].to;
    						delet(v,a[u]);//u被改变之后,它的邻居的值集也会变化
    						insert(v,x);
    					}
    					a[u]=x;
    				}
    			}
    			else {
    				for (int i=0; i<big_ngb[u].size(); i++){
    					//遍历邻居中的大点,因为大点还没有被改变,所以我们要将他改变
    					int v=big_ngb[u][i].first;
    					int pre_a=big_ngb[u][i].second;//大点的旧值
    					if (pre_a!=a[v]){
    						delet(u,pre_a);
    						insert(u,a[v]);
    						big_ngb[u][i].second=a[v];
    					}
    				}
    				printf("%d
    ",query(u));
    			}
    		}
    	}
    	return 0;
    }
    
    路漫漫兮
  • 相关阅读:
    OLEDB 枚举数据源
    OLEDB 调用存储过程
    OLEDB 参数化查询
    多结果集IMultipleResult接口
    使用pyh生成HTML文档
    数据更新接口与延迟更新
    SQL语句执行与结果集的获取
    事务对象和命令对象
    DNS练习之反向解析
    DNS练习之正向解析
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13379683.html
Copyright © 2011-2022 走看看