zzulioj 1916 (DFS序 树状数组)

Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 228 Solved: 20

SubmitStatusWeb Board
Description

给一颗树,有n个结点,编号为1到n,1为根节点,有两种操作,1 x y把x结点权值加y,2 x查询x到根节点所有结点的权值和.
每个结点权值初始化为0。

Input

第一行输入一个整数t,代表有t组测试数据。
每组数据第一行为两个整数n,m代表结点个数和操作次数。
接下来n-1行,每行两个整数a,b,表示a结点和b结点有一条边。
接下来m行每行一个操作格式如上。
0<=n<=50000 ,0<=m<=50000,0<=y<=50000

Output

对于每组查询输出一个整数表示结果

Sample Input

1
5 5
1 2
1 3
3 4
3 5
2 5
1 1 2
1 3 1
2 4
2 1
Sample Output

0
3
2
第一次听说这个什么DFS序的,原来树状数组还有这么个用法,读书少啊
大神博客推荐点此进入
这个东西,自己把树状数组画出来以后就会发现很奇妙的东西,就是no[i].up和no[i].dowm之间的区间是对称的,左边的总是比右边的大,因此query(no[i].up) - query[no[j].up - 1] 区间的距离就是树的点 i 到点 j 的权值之和,而这个query(n<<1) == query(no[1].up),就是顶点咯;

#include 
#include 
#include 
#include 
using namespace std;
#define LL long long
#define M 50050
#define INF 0x3f3f3f
vector<int> vec[M<<1];
int n, id;
LL tree[M<<1];
struct node
{
    int up, down;
};
node no[M];
int lowbit(int x)
{
    return x & (-x);
}
void addtree(int x, int add)
{
    while(x <= (n<<1))
    {
        tree[x] += add;
        x += lowbit(x);
    }
}
LL query(int x)
{
    LL sum = 0;
    while(x > 0)
    {
        sum += tree[x];
        x -= lowbit(x);
    }
    return sum;
}
void serch(int x, int fa)//遍历赋值,定区间
{
    no[x].down = ++id;//注意底层先加,区间在左,值为负
    for(int i=0; iif(vec[x][i] != fa)
        {
            serch(vec[x][i], x);
        }
    }
    no[x].up = ++id;
}
int main()
{
    int t, a, b, m;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i=0; imemset(tree, 0, sizeof(tree));
        for(int i=1; iscanf("%d%d", &a, &b);
            vec[a].push_back(b);
            vec[b].push_back(a);
        }
        id = 0;
        serch(1, 0);
        int flag = 0;
        for(int i=1; i<=m; i++)
        {
            scanf("%d", &flag);
            if(flag == 1)
            {
                scanf("%d%d", &a, &b);
                addtree(no[a].down, -b);//负与正并存
                addtree(no[a].up, b);
                //根据正负之间的消除可以巧妙去掉没有走过的点
                //up 与 dowm 之间的点都是其包含的点,否则都是与其并列的点,并列的点在左边不会加入,在右边右会因为正负而消除,
            }
            else
            {
                scanf("%d", &a);
                printf("%lld\n", query(n<<1) - query(no[a].up - 1));
            }
        }
    }


    return 0;
}

你可能感兴趣的:(算法)