Ka Chang 沈阳网络赛 (树上分块+dfs序时间戳+树状数组 )

Given a rooted tree ( the root is node 11 ) of NN nodes. Initially, each node has zero point.

Then, you need to handle QQ operations. There're two types:

1\ L\ X1 L X: Increase points by XX of all nodes whose depth equals LL ( the depth of the root is zero ). (x \leq 10^8)(x≤108)

2\ X2 X: Output sum of all points in the subtree whose root is XX.

Input

Just one case.

The first lines contain two integer, N,QN,Q. (N \leq 10^5, Q \leq 10^5)(N≤105,Q≤105).

The next n-1n−1 lines: Each line has two integer aa,bb, means that node aa is the father of node bb. It's guaranteed that the input data forms a rooted tree and node 11 is the root of it.

The next QQ lines are queries.

Output

For each query 22, you should output a number means answer.

样例输入复制

3 3
1 2
2 3
1 1 1
2 1
2 3

样例输出复制

1
0

题目来源

ACM-ICPC 2018 沈阳赛区网络预赛

题意:有两个操作,操作1是对于一个深度l上面的结点的权值增加v,操作2是对于一个结点求和它的子树

题解:首先可以考虑可以维护一个树状数组来维护子树的权值和,但是若同一层结点太多那必然超时,则可以考虑分块的思想来分开讨论,对于同一深度设一个阈值limit,若这一深度中结点的个数>limit,则不实际进行更改,而是用一个数组来标记,因为>limit的层数不会太多,所以可以直接暴力枚举所有超越阈值的层数对于答案的贡献,同时二分答案即可。在这里遇到了一个我以前没接触到的一个知识点,如何判断一个点是另一个点子树中的点,一个新的知识点--DFS序+时间戳,记录一个结点在dfs中第一次出现的时间和最后一次出现的时间若一个结点时间的区间是另一个结点时间区间的子区间(同时区间不可能相交),则这个点为另一个点子树的子节点,根据这个性质就可以进行维护树状数组了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
typedef long long ll;

vector<int>G[maxn],dep[maxn],large;
int st[maxn],ed[maxn],tim,n;
ll tree[maxn],num[maxn];

void add(int pos,ll v)
{
    for(;pos<=n;pos+=pos&-pos)
        tree[pos]+=v;
}

ll query(int pos)
{
    ll ans=0;
    for(;pos;pos-=pos&-pos)
        ans+=tree[pos];
    return ans;
}

void dfs(int u,int fa,int depth) //时间戳
{
    st[u]=++tim;
    dep[depth].push_back(tim);
    int sz=G[u].size();
    for(int i=0;i<sz;i++)
        if(G[u][i]!=fa)
            dfs(G[u][i],u,depth+1);
    ed[u]=tim;
}

int main()
{
    int q;
    scanf("%d%d",&n,&q);
    for(int i=0;i<n-1;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,0,0);

    int block=ceil(sqrt(n));
    for(int i=0;i<n;i++)
        if(dep[i].size()>block)
            large.push_back(i);

    while(q--)
    {
        int nood;
        scanf("%d",&nood);
        if(nood==1)
        {
            int depth;
            ll v;
            scanf("%d%lld",&depth,&v);
            if(dep[depth].size()>block) num[depth]+=v;
            else
            {
                for(int i=0;i<dep[depth].size();i++)
                    add(dep[depth][i],v);
            }
        }
        else
        {
            int x;
            scanf("%d",&x);
            ll ans=query(ed[x])-query(st[x]-1);
            for(int i=0;i<large.size();i++)
                ans+=(upper_bound(dep[large[i]].begin(),dep[large[i]].end(),ed[x])-lower_bound(dep[large[i]].begin(),dep[large[i]].end(),st[x]))*num[large[i]];
            printf("%lld\n",ans);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值