杭电第一场1005 Finding a MEX 树上分块
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6756
题目大意:
有一个n点,m条边的无向图,每个点有一个权值。现在有两个操作:
1 x y:把x点的权值修改为y
2 x : 查询x的的邻接点的权值中没有出现的最小非负整数。
思路:前几天正好补了一个分块的题,今天就遇到了。
我们考虑按点的度数进行维护。
我们考虑用权值线段树去维护每个点的mex。
对于修改x:
我们去遍历它d[y]>sqrt(n)的邻接点,最多有sqrt(n)个,每个在线段树上修改,复杂度:O(sqrt(n) * logn)
对于查询x:
如果d[u]<=sqrt(n)那么我们暴力每个点查询就可以了。复杂度:O(sqrt(n))
如果d[u]>sqrt(n)那么我们直接在线段树查询就可以了,复杂度:O(sqrt(n) * logn)
如果就一直T到怀疑人生。这个地方用线段树常树太大。
我们考虑一些其他的维护方法:
1.树状数组(sqrt(n) * logn):二分结果,前缀和查询。因为添加是O(sqrt(n) * logn)和查询(logn * log n)是分开的,那么复杂度还是添加的:sqrt(n) * logn
2.树状数组 (sqrt(n) * logn):我们可以直接在树上二分,标程的做法
3.set(sqrt(n) * logn):保存没有被占用的点。添加为set中删除,删除为set中添加。
3.分块(sqrt(n)): 我们根据上面的复杂度分析可以知道如果数据结构修改是O(1),查询是O(sqrt(n)),那么总的复杂度就是O(n * sqrt(n))。
具体做法:
我们按权值分块,用laz数组维护这个块非o的数量。用桶记一下个数,O(1)修改
查询到每一块if laz[i]!=len, 说明这个块有没有被占用的,去块里找,复杂度:O(sqrt(n))
#include <bits/stdc++.h> using namespace std; int f[200015], d[200015]; vector<int> G[200015], son[200015], pos; int vis[200015]; struct FK{ vector<int> w[200015], laz[200015]; int len; void init(int n){ len=sqrt(n)+1; for(int i=1; i<=n; i++){ w[i].clear(); w[i].resize(2*d[i]+5); laz[i].clear(); laz[i].resize(2*d[i]+5); } for(int i=1; i<=n; i++){ if(d[i]>len){ int siz=d[i]/len; for(int k=0; k<=siz; k++){ laz[i][k]=len; } } } } void up_data(int pos, int x, int y){//x->y if(x<=d[pos]){ w[pos][x]--; if(w[pos][x]==0){ laz[pos][x/len]++; } } if(y<=d[pos]){ w[pos][y]++; if(w[pos][y]==1){ laz[pos][y/len]--; } } } int qurey(int pos){ int siz=d[pos]/len; int p=siz; for(int i=0; i<=siz; i++){ if(laz[pos][i]){ p=i; break; } } for(int i=len*p; i<len*(p+1); i++){ if(!w[pos][i]){ return i; } } } }F; int main(){ int t; scanf("%d", &t); while(t--){ int n, m, q, x, y, z; scanf("%d%d", &n, &m); for(int i=1; i<=n; i++){ f[i]=d[i]=0; G[i].clear(); son[i].clear(); } for(int i=1; i<=n; i++){ scanf("%d", &f[i]); } for(int i=1; i<=m; i++){ scanf("%d%d", &x, &y); G[x].push_back(y); G[y].push_back(x); d[x]++, d[y]++; } for(int i=1; i<=n; i++){ sort(G[i].begin(), G[i].end(), [](int &a, int &b){return d[a]<d[b];}); } int len=sqrt(n)+1; F.init(n); for(int i=1; i<=n; i++){ for(auto x: G[i]){ if(d[x]>len){ son[i].push_back(x); } } } for(int i=1; i<=n; i++){ for(auto x: son[i]){ F.up_data(x, 1e5+5, f[i]); } } scanf("%d", &q); while(q--){ scanf("%d%d", &x, &y); if(x==1){ scanf("%d", &z); for(auto x: son[y]){ if(d[x]>len){ F.up_data(x, f[y], z); } } f[y]=z; } else{ if(d[y]>len){ for(auto x: son[y]){ F.up_data(x, 1e5+5, f[x]); } printf("%d\n", F.qurey(y)); for(auto x: son[y]){ F.up_data(x, f[x], 1e5+5); } } else{ for(int i=0; i<G[y].size(); i++){ if(f[G[y][i]]<n+5){ pos.push_back(f[G[y][i]]); vis[f[G[y][i]]]=1; } } for(int i=0; i<=n+5; i++){ if(!vis[i]){ printf("%d\n", i); break; } } for(auto x: pos){ vis[x]=0; } pos.clear(); } } } } return 0; }