2020杭电多校第一场1006 / HDU 6756 (分块 / 树状数组 + 二分)

Description
Given an undirected graph G = ( V , E ) G=(V,E) G=(V,E). All vertices are numbered from 1 1 1 to N N N. And every vertex u has a value of A u A_u Au. Let S u S_u Su= { A v │ ( u , v ) ∈ E } \left\{ Av│(u,v)∈E\right\} {Av(u,v)E}. Also, F ( u ) F(u) F(u) equals MEX(minimum excludant) value of S u S_u 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 A u A_u Au to x ( 0 ≤ x ≤ 1 0 9 ) (0≤x≤10^9) (0x109).
Type 2: 2 u – Calculate the value of F ( u ) F(u) 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 ) T ~(1≤T≤10) T (1T10) denoting the number of test cases. Each test case begins with a single line containing two integers n   ( 1 ≤ n ≤ 1 0 5 ) n~(1≤n≤10^5) n (1n105), m   ( 1 ≤ m ≤ 1 0 5 ) m~(1≤m≤10^5) m (1m105) 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 A i ( 0 ≤ A i ≤ 1 0 9 ) A_i (0≤A_i≤10^9) Ai(0Ai109).

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 ≤ 1 0 5 ) q~(1≤q≤10^5) q (1q105) 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 ) F(u) F(u) in a single line.

Examples
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

Source
2020 Multi-University Training Contest 1

Main idea
给你一张图,每个点有权值,操作一为修改某一点权值;操作二为询问某一点相邻点点权的MEX

Solution 1
纯分块

注意到一个点的Mex不可能超过其度数,我们按照每个点的度数大小将所有点分为:
“大点” :度数大于 m \sqrt{m} m 的点 (其个数不会超过 m \sqrt{m} m )
“小点” :度数不大于 m \sqrt{m} m 的点

将大点的相邻点的权值分块,维护每个块内有多少个不同的数
更新:撤销原值的影响 + 更新新值的影响 维护u相邻的所有大点的块内信息 O ( m ) O(\sqrt{m}) O(m )
查询:小点暴力 O ( m ) O(\sqrt{m}) O(m );大点也暴力(分块) O ( m ) O(\sqrt{m}) O(m )

分块求MEX : 维护每个块内有多少不同的数,从小到大找块,若块不满,就进去找缺失的最小值

Solution 2
分块 + 树状数组 + 二分

点依然按照 m \sqrt{m} m 分块,只是用树状数组来维护大点相邻点不同权值个数,查询时套上二分

更新:树状数组单点修改 O ( log ⁡ n ) O(\log{n}) O(logn)
查询:小点暴力 O ( m ) O(\sqrt{m}) O(m ) ; 大点二分+树状数组区间查询 O ( ( log ⁡ n ) 2 ) O((\log{n})^2) O((logn)2)

Code 1

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int maxn = 1e5 + 5;
const int maxp = 320;
int n,m,a[maxn];
vector<int>Edge[maxn];
vector<int>mx[maxn];
int lim,tot,d[maxn],id[maxn];
int blksiz[maxn],mxblk[maxn];
vector<int>blk[maxp];//权值所对应的blk编号
vector<int>cnt[maxp];//权值出现次数
vector<int>siz[maxp];//blk内不同权值个数

inline int get_L(int u,int x){return (x-1) * blksiz[id[u]];}
inline int get_R(int u,int x){return x == mxblk[id[u]] ? d[u] - 1 : x * blksiz[id[u]] - 1;}
inline void update(int u,int x){
	for(auto v : mx[u]){
		if(a[u] < d[v]) if(--cnt[id[v]][a[u]] == 0) siz[id[v]][blk[id[v]][a[u]]]--;
		if(x < d[v]) if(++cnt[id[v]][x] == 1) siz[id[v]][blk[id[v]][x]]++;
	}
	a[u] = x;
}

bool vis[maxp];
inline int query(int u){
	//小点暴力
	//O(sqrt(n))
	if(d[u] <= lim){
		for(int i = 0;i < d[u];++i) vis[i] = false;
		for(auto v: Edge[u]) if(a[v] < d[u]) vis[a[v]] = true;
		for(int i = 0;i < d[u];++i) if(!vis[i]) return i;
		return d[u];
	} else {
		//大点也暴力(分块)
		//O(sqrt(n))
		for(int i = 1;i <= mxblk[id[u]];++i){
			if(siz[id[u]][i] != get_R(u,i) - get_L(u,i) + 1){
				int L = get_L(u,i), R = get_R(u,i);
				for(int j = L;j <= R;++j)
					if(!cnt[id[u]][j]) return j;
				break;
			}
		}
		return d[u];
	}
}

inline void init(){
	tot = 0;
	for(int i = 0;i <= n;++i){
		Edge[i].clear();mx[i].clear();
		d[i] = 0;id[i] = 0;
	}
	for(int i = 0;i < maxp;++i){
		blk[i].clear();cnt[i].clear();siz[i].clear();
		blksiz[i] = mxblk[i] = 0;
	}
}

int main(){
	int T;scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);init();
		for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
		for(int i = 1,u,v;i <= m;++i){
			scanf("%d%d",&u,&v);
			Edge[u].pb(v);Edge[v].pb(u);
			d[u]++, d[v]++;
		} lim = sqrt(2.0*m);
		for(int i = 1;i <= n;++i){
			for(auto v : Edge[i]){
				if(d[v] > lim) mx[i].pb(v);
			}
			if(d[i] <= lim) continue;
			id[i] = ++tot;
			blksiz[tot] = (int)sqrt((double)(d[i]-1));
			blk[tot].assign(d[i] + 3,0);
			cnt[tot].assign(d[i] + 3,0);
			siz[tot].assign(blksiz[tot] + 3,0);
			for(int j = 0;j < d[i];++j) blk[tot][j] = j / blksiz[tot] + 1;
			mxblk[tot] = blk[tot][d[i]-1];
			for(auto v : Edge[i]){
				if(a[v] >= d[i]) continue;
				if(++cnt[tot][a[v]] == 1) siz[tot][blk[tot][a[v]]]++;
			}
		}
		int q;scanf("%d",&q);
		while(q--){
			int op,u;scanf("%d%d",&op,&u);
			if(op == 1){
				int x;scanf("%d",&x);
				update(u,x);
			} else if(op == 2) printf("%d\n", query(u));
		}
	}
    return 0;
}

Code 2

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;

const int maxn = 2e5 + 5;
const int maxp = 500;
inline int lowbit(int x){return x &(-x);}
int n,m,a[maxn];
vector<int>bit[maxp];
vector<int>Edge[maxn];
vector<int>cnt[maxp];
vector<int>node[maxn];
int d[maxn];
int tot,lim,id[maxn];

inline void add(int x,int pos,int v){
    while(pos < d[x]){
        bit[id[x]][pos] += v;pos += lowbit(pos);
    }
}
inline int ask(int x,int pos){
    int res = 0;
    while(pos > 0){
        res += bit[id[x]][pos];pos -= lowbit(pos);
    } return res;
}
inline void update(int u,int x){
    for(auto v : node[u]){
        if(a[u] < d[v]) if(--cnt[id[v]][a[u]] == 0 && a[u]) add(v,a[u],-1);
        if(x < d[v]) if(++cnt[id[v]][x] == 1 && x) add(v,x,1);
    }
    a[u] = x;
}

bool vis[maxp];
inline int query(int u){
    if(d[u] <= lim){
        for(int i = 0;i < d[u];++i) vis[i] = false;
        for(auto v : Edge[u]){
            if(a[v] < d[u]) vis[a[v]] = true;
        }
        for(int i = 0;i < d[u];++i){
            if(!vis[i]) return i;
        }
        return d[u];
    } else{
        if(!cnt[id[u]][0]) return 0;
        int l = 1, r = d[u], mex = d[u];
        while(l <= r){
            int mid = (l + r) >> 1;
            if(ask(u,mid) < mid) mex = mid, r = mid - 1;
            else l = mid + 1;
        }
        return mex;
    }
}
void init(){
    tot = 0;
    for(int i = 0;i <= n;++i) Edge[i].clear(), node[i].clear(), d[i] = 0;
    for(int i = 0;i < maxp;++i){
        cnt[i].clear();bit[i].clear();
    }
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);init();
        for(int i = 1;i <= n;++i) scanf("%d",&a[i]);
        for(int i = 1,u,v;i <= m;++i){
            scanf("%d%d",&u,&v);Edge[u].pb(v);Edge[v].pb(u);
            d[u]++, d[v]++;
        }
        lim = sqrt(m);
        for(int i = 1;i <= n;++i){
            if(d[i] <= lim) continue;
            id[i] = ++tot;
            bit[tot].assign(d[i] + 10,0);
            cnt[tot].assign(d[i] + 10,0);
            for(auto v: Edge[i]){
                if(a[v] > d[i]) continue;
                cnt[tot][a[v]]++;
                if(cnt[tot][a[v]] == 1 && a[v]) add(i,a[v],1);
            }
        }
        for(int i = 1;i <= n;++i){
            for(auto v:Edge[i]){
                if(d[v] > lim) node[i].pb(v);
            }
        }
        int q;scanf("%d",&q);
        while(q--){
            int op,u,x;scanf("%d%d",&op,&u);
            if(op == 1){
                scanf("%d",&x); update(u,x);
            } else if(op == 2){
                printf("%d\n", query(u));
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值