这道题展示了分块的强大,学到一手,虽然因为数据太过友好暴力也能过
这道题边数多,直接遍历复杂度很高,大佬们想到了一种分摊复杂度的方法
对于入度大于指定值例如(sqrt),这也是分块常用指定值的点,我们定义为重点
否则为轻点,重点只和重点连,轻点和轻点连。这基于的原理是,重点的个数不超过sqrt个,并且轻点连的边数也不超过sqrt个
对于每个点我们维护val也就是自身加的值,以及sum,周围点的和,对于查询,重点直接输出sum,而轻点进行遍历。
正确的原因是,首先轻点肯定正确,因为是暴力,对于重点来说,所有对于轻点更新的大小,都会通过轻点连向重点的边更新到重点上
同理,重点之间也能相互传递,所以求出sum就是答案。而轻点不能直接输出sum,因为对重点的更新无法传递到轻点,但是因为边数控制住了
所以遍历的复杂度依旧不高。
#include<iostream> #include<cstdio> #include<vector> #include<algorithm> #include<cmath> using namespace std; const int N=2e5+20; typedef long long ll; int sum[N],val[N]; int in[N]; int st[N]; vector<int> g[N]; int n,m; struct node{ int u,v; }s[N]; void init(){ int i; for(i=0;i<=n;i++){ sum[i]=val[i]=in[i]=0; g[i].clear(); } } int main(){ int t; cin>>t; while(t--){ int i; cin>>n>>m; init(); for(i=1;i<=m;i++){ scanf("%d%d",&s[i].u,&s[i].v); in[s[i].u]++; in[s[i].v]++; } int block=sqrt(n); for(i=1;i<=n;i++){ if(in[i]>block) st[i]=1; } for(i=1;i<=m;i++){ int u=s[i].u; int v=s[i].v; if(st[u]){ if(st[v]){ g[u].push_back(v); g[v].push_back(u); } else{ g[v].push_back(u); } } else{ if(st[v]){ g[u].push_back(v); } else{ g[u].push_back(v); g[v].push_back(u); } } } int q; cin>>q; while(q--){ int cmd; scanf("%d",&cmd); if(cmd){ int u; scanf("%d",&u); if(st[u]){ printf("%d\n",sum[u]); } else{ int ans=0; for(i=0;i<g[u].size();i++){ ans+=val[g[u][i]]; } printf("%d\n",ans); } } else{ int u,v; scanf("%d%d",&u,&v); val[u]+=v; for(i=0;i<g[u].size();i++){ int tmp=g[u][i]; sum[tmp]+=v; } } } } }