目录
Description
有 \(n\) 个节点,有 \(m\) 条边,有 \(Q\) 次操作
\(0\ u\ v\): 节点 \(u\) 的权值增加 \(v\)
\(1\ u\): 输出与 \(u\) 相连的节点所有节点之和
State
\(1<=n<=10^5\)
\(1<=m<=n+10\)
\(1<=v<=100\)
Input
1
3 2
1 2
1 3
6
0 1 15
0 3 4
1 1
1 3
0 2 33
1 2
Output
4
15
15
Solution
图中有两种节点,一种是节点度小于等于 \(\sqrt{m}\) 的,另一种是大于 \(\sqrt{m}\) 的;
而对于操作 $ 0$ 时间复杂度为 \(O(1)\),操作 \(1\) 最坏为 \(O(m)\)
这显然是不可取的;但是对于第一种节点的查询操作为 \(O(\sqrt{m})\),如果将第二种节点查询复杂度降下来的话,复杂度应该是可行的
这时只关心第二种节点,与第二种节点相连的仍然是这两种节点。
对于一个重结点 \(x\) ,与其相连的节点更新时如果想要波及到 \(x\) 的话,这两种点都要连接 \(x\),也就是说:
轻节点只要连接重节点,重节点只要连接重节点,在更新时就可以满足时间上的要求了
在这里轻节点的度显然小于等于 \(\sqrt{m}\),而重节点最多有 \(\sqrt{m}\) 个,所以重节点的度也为 \(\sqrt{m}\)
Code
const int N = 1e5 + 5;
int n, m, k, _;
vector<int> G[N], G2[N];
int block;
int sum[N], ans[N];
void clear()
{
for(int i = 1; i <= n; i ++){
G[i].clear();
G2[i].clear();
sum[i] = ans[i] = 0;
}
}
signed main()
{
// IOS;
rush(){
sdd(n, m);
int x, y;
rep(i, 1, m){
sdd(x, y);
G[x].pb(y);
G[y].pb(x);
}
block = sqrt(m);
for(int i = 1; i <= n; i ++){
for(int j = 0; j < G[i].size(); j ++){
x = G[i][j];
if(G[x].size() > block) G2[i].pb(x);
}
}
int Q = read();
for(int k = 1; k <= Q; k ++){
int opt, u, v;
sdd(opt, u);
if(opt == 0){
sd(v);
ans[u] += v;
for(auto it : G2[u]){
sum[it] += v;
}
}
else{
int res = 0;
if(G[u].size() <= block){
for(auto it : G[u]){
res += ans[it];
}
}
else{
res = sum[u];
}
pd(res);
}
}
clear();
}
// PAUSE;
return 0;
}