ACM-ICPC 2018 徐州赛区网络预赛 J(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 沈阳赛区网络预赛

 

题意:一颗树,没个节点的值一开始都为0,现在有2种操作,第一种第L层每个节点的值增加x,第二种查询x为根的子树的权值和。

思路:dfs序+树状数组或者线段树是很快可以想到的,问题在于如果直接单点更新那么每次更新操作都是O(nlogn)的复杂度查询O(logn),又如果每次都是把每层增加的权值记录下来,查询的时候再找出这个子树中每个层的节点数量,那么每次更新复杂度O(1)查询复杂度是O(nlogn)的,所以把两者结合起来,对于节点数>sqrt(n)的层记录,反之直接单点更新。如此分块的话,每次的查询和查询复杂度都为O(sqrt(n)logn)。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<math.h>
#include<algorithm>
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int n,q;
int cnt = 0;
int dfn[maxn];  //dfs序
int idc[maxn];  //每个节点在dfs序中的位置
int mp[maxn]; 
int rmp[maxn];
ll add[maxn];   //记录重层的增加量
int maxl;
vector<int>vec[maxn];
vector<int>level[maxn];   //每层的节点
vector<int>leveldfn[maxn];   //每层节点对应的的方式序
struct node
{
    int sta,end;
}subtr[maxn];
void dfs(int x,int lvl)
{
    dfn[++cnt] = x;
    maxl = max(maxl,lvl);
    level[lvl].push_back(x);
    leveldfn[lvl].push_back(cnt);  
    idc[x] = cnt;
    subtr[x].sta = cnt;
    for(int i =0;i<vec[x].size();i++)
    {
        dfs(vec[x][i],lvl+1);
    }
    subtr[x].end = cnt;
}
ll c[maxn];   
int Lowbit(int x)  
{  
    return x&(-x);  
}  
void update(int i, ll x) 
{  
    while(i <= n)  
    {  
        c[i] += x;  
        i += Lowbit(i);  
    }  
}  
ll sum(int x)
{  
    ll sum=0;  
    while(x>0)  
    {  
        sum+=c[x];  
        x-=Lowbit(x);  
    }  
    return sum;  
}   
ll Getsum(int x1,int x2)  
{   
    return sum(x2) - sum(x1-1);   
} 
int main()
{
    maxl = 0;
    cnt = 0;
    scanf("%d%d",&n,&q);
    for(int i = 0;i<=n;i++)
    {
        vec[i].clear();
        level[i].clear();
        leveldfn[i].clear();
    }
    memset(add,0,sizeof(add));
    memset(c,0,sizeof(c));
    int u,v;
    for(int i = 0;i<n-1;i++)
    {
        scanf("%d%d",&u,&v);
        vec[u].push_back(v);
    }
    dfs(1,0);
    int sqrtn = sqrt(n);
    int addcnt = 0;
    for(int i = 0;i<=maxl;i++)
    {
        if(level[i].size()>sqrtn)
        {
            mp[i] = addcnt;
            rmp[addcnt++] = i;
        }
    }
    int opt;
    while(q--)
    {
        scanf("%d",&opt);
        if(opt==1)
        {
            int L;
            ll x;
            scanf("%d%lld",&L,&x);
            if(level[L].size()<=sqrtn)
            {
                for(int i = 0;i<level[L].size();i++)
                {
                    update(idc[level[L][i]],x);
                }
            }
            else
            {
                add[mp[L]] += x;
            }
        }
        else
        {
            int x;
            scanf("%d",&x);
            ll ans = Getsum(subtr[x].sta,subtr[x].end);
            int L;
            for(int i = 0;i<addcnt;i++)
            {
                L = rmp[i];
                int l = lower_bound(leveldfn[L].begin(),leveldfn[L].end(),subtr[x].sta) - leveldfn[L].begin();
                int r = upper_bound(leveldfn[L].begin(),leveldfn[L].end(),subtr[x].end) - leveldfn[L].begin();
                ans += (r - l)*add[i];
            }
            printf("%lld\n",ans);
        }
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值