一看到这题,首先会觉得非常像单点更新的线段树,但是却不怎么好操作
然后应该往分块的方向去想
因为只有m条边,所以所有点的度总和是2m,那么设度数>=sqrt(m)的点叫做重点,反之则是轻点
那么重点的个数<=2m/sqrt(m)=2sqrt(m)
设A[i]表示该点的值,sum[i]表示该点周围相连的点的A[i]之和
构造图的时候也有技巧,对于一条边(u,v)
如果u是轻点,直接添加(u,v)这条边 //因为边数不会超过sqrt(m)条
如果u是重点,那么v也是重点时候,才添加(u,v)这条边 //因为重点个数小于2sqrt(m),所以这里添加的边也不算特别多
更新时
对于每个点,先更新自己,A[u]+=d,遍历其所有的边,使sum[v]+=d
实际上本来如果v是轻点,是可以不用操作的,但是也无所谓啦,反正只有u也是轻点的时候,v才可能是轻点,但是u的边数已经很小了,加不加判断都无所谓
操作了也并不会影响答案,因为如果i是轻点,sum[i]是没用的,等下继续讲
查询时
如果x是重点,由之前的构图和更新能知道,对于任意v是重点边,都是添加在图里面的,所以sum[x]就是答案
如果x是轻点,实际上sum[x]是没意义的,但是与x连接的边数<=sqrt(m),所以直接枚举所有周围的点,积累周围点的A值即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const int mod=1e9+7;
const int MX=100100+5;
const int INF=0x3f3f3f3f;
struct Edge{
int u,v;
}E[MX];
int n,m,unit,Q;
int P[MX],sum[MX],A[MX];
vector<int>G[MX];
void update(int x,int d){
A[x]+=d;
for(int i=0;i<G[x].size();i++){
int nxt=G[x][i];
sum[nxt]+=d;
}
}
int query(int x){
if(P[x]<unit){
sum[x]=0;
for(int i=0;i<G[x].size();i++){
int nxt=G[x][i];
sum[x]+=A[nxt];
}
return sum[x];
}else{
return sum[x];
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(A,0,sizeof(A));
memset(P,0,sizeof(P));
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++) G[i].clear();
for(int i=1;i<=m;i++){
scanf("%d%d",&E[i].u,&E[i].v);
P[E[i].u]++;
P[E[i].v]++;
}
unit=sqrt(m+0.5);
for(int i=1;i<=m;i++){
int u=E[i].u,v=E[i].v;
if(P[u]<unit) G[u].push_back(v);
else if(P[v]>=unit) G[u].push_back(v);
if(P[v]<unit) G[v].push_back(u);
else if(P[u]>=unit) G[v].push_back(u);
}
scanf("%d",&Q);
while(Q--){
int cmd,a,b;
scanf("%d",&cmd);
if(cmd==0){
scanf("%d%d",&a,&b);
update(a,b);
}
else{
scanf("%d",&a);
printf("%d\n",query(a));
}
}
}
return 0;
}