目录

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;
}