2020杭电多校(一) Finding a MEX(分块)
比赛的时候根据数据范围马上猜出了是通过轻重点分块,但是在维护set的时候犯了一个nt错误
选择了插入邻边的权值去判断第一个未出现的答案,怎么想都觉得复杂度很高。
其实可以选择先插入所有得可能答案,然后对邻边影响得答案进行删除,这样set中得第一个元素就是答案。
这题复杂度保证得原因是,通过分块,对于边数大于1000的称为重点,去维护一个set,因为边总共只有1e5条,这样的点不会超过100个,并且只需要维护从0-度数这样长度的set就行
后面的值肯定不会影响答案。
对于轻点,只需要暴力枚举所有的边去暴力判断,因为轻点邻边不会超过1000条,所以复杂度也得到保证
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; const int mod=998244353; vector<int> g[N]; set<int> s[N]; int in[N]; int num[110][N]; int id[N]; bool st[N]; int a[N]; vector<int> now[N]; int main(){ ios::sync_with_stdio(false); int i; int t; cin>>t; while(t--){ int n,m; cin>>n>>m; int i; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=n;i++){ g[i].clear(); in[i]=0; now[i].clear(); } int block=1000; for(i=1;i<=m;i++){ int x,y; cin>>x>>y; g[x].push_back(y); g[y].push_back(x); in[x]++,in[y]++; } int cnt=0; for(i=1;i<=n;i++){ if(in[i]>=block){ id[i]=++cnt;//ÀëÉ¢»¯Ó³Éä int j; s[cnt].clear(); for(j=0;j<=in[i];j++){ s[cnt].insert(j),num[cnt][j]=0; } } } for(i=1;i<=n;i++){ int j; for(j=0;j<g[i].size();j++){ int u=g[i][j]; if(in[u]>=block){ if(a[i]<=in[u]){ if(++num[id[u]][a[i]]==1){ s[id[u]].erase(a[i]); } now[i].push_back(u); } } } } int q; cin>>q; while(q--){ int opt; cin>>opt; if(opt==1){ int x,y; cin>>x>>y; for(auto tmp:now[x]){ if(y<=in[tmp]){ if(++num[id[tmp]][y]==1) s[id[tmp]].erase(y); } if(a[x]<=in[tmp]){ if(--num[id[tmp]][a[x]]==0){ s[id[tmp]].insert(a[x]); } } } a[x]=y; } else{ int x; cin>>x; if(in[x]>=block){ cout<<*s[id[x]].begin()<<endl; } else{ for(i=0;i<g[x].size();i++){ int v=g[x][i]; if(a[v]<=in[x]){ st[a[v]]=1; } } int ans=0; while(st[ans]) ans++; cout<<ans<<endl; for(i=0;i<g[x].size();i++){ int v=g[x][i]; if(a[v]<=in[x]){ st[a[v]]=0; } } } } } } return 0; }
没有人不辛苦,只有人不喊疼