题意:一棵苹果树有n个结点,编号从1到n,根结点永远是1。该树有n-1条树枝,每条树枝连接两个结点。已知苹果只会结在树的结点处,而且每个结点最多只能结1个苹果。初始时每个结点处都有1个苹果。树的主人接下来会进行m个操作。操作共两种。C X表示将结点x上的苹果数量改变,原本是1,则现在为0,原本是0,现在是1。Q X表示一次查询。要求输出结点X和其子树上的苹果总数。n和m最大可到100000。

操作只有更新和查询两种,树状数组最合适了。

首先是树状数组的相关知识。网上有很多讲解,在这里传送一个讲解的地址 传送门

树状数组最重要的就是要搞明白那种经典的图,之后就没什么问题了。

思路:本题的关键是如何将树映射成线性的数组。而且树状数组一般是对连续区间求和,又依照题意的要求,树的子树要在区间内也是连续存储的。这里的方法是,采用dfs对树进行一次遍历,树的每一个结点都有st和ed两个时间戳,分别记录该结点被遍历到的时间戳以及它和它的子树全部遍历完后的时间戳。举一个例子来说明。

依次遍历到的结点:1  5  4  3  2

对应的时间戳:1  2  3  4  5

拿结点4来说,它的开始时间戳st为3,结束时间戳ed为5。

这样的话,假如需要询问结点x和它子树上的苹果总数,只需对区间[st[x], ed[x]]求和即可。另外需要注意的是,树状数组求和函数query求的是区间[1, x]的和,因此要实现之前的求和,需要用query(ed[x]) - query(st[x] - 1)。 (query(0) = 0)

以上就是解题思路了。

此外要注意,在建图的时候,添加边应该是双向边(即无向边),不然在遍历时会出现遍历不到或者其他问题。一开始我提交了两次总是tle,问题就在这里。

至于树状数组更新的时候,假设更新位置为x,则应将后续的x += lowbit[x]的位置也更新,直到x大于n。做这题时,我以为只要更新到结点x的结束位置ed[x]即可,但基于树状数组的特点,x变化后,后续结点即使不在x的子树里也是有可能受影响的,应当更新。不然在求和时就会得出错误结果。

 #include<stdio.h>
#include<string.h>
#define maxn 100020
#define maxp 200020
struct node
{
int v;
int next;
}edge[maxp];
int num_edge, head[maxn];
void addedge(int a, int b)
{
edge[num_edge].v = b;
edge[num_edge].next = head[a];
head[a] = num_edge++;
}
void init_edge()
{
num_edge=;
memset(head,-,sizeof(head));
} int st[maxn], ed[maxn], vis[maxn], cnt;//cnt记录时间戳,初始为0
void get_timestamp(int u)
{
vis[u] = ;
st[u] = ++cnt;//记录开始时间戳
for (int i = head[u]; i != -; i = edge[i].next)
{
int v = edge[i].v;
if (!vis[v]) get_timestamp(v);
}
ed[u] = cnt;//记录结束时间戳
} int lowbit[maxn], apple[maxn];
int n;//fork的总数
void update(int x,int num)
{
for (int i = x; i <= n; i += lowbit[i])
apple[i] += num;
}
int query(int x)
{
int res = ;
for (int i = x; i > ; i -= lowbit[i])
res += apple[i];
return res;
}
int main()
{
//freopen("data.in", "r", stdin);
scanf("%d",&n);
init_edge();
for (int i = ; i < n; i++)
{
int u, v;
scanf("%d%d",&u,&v);
addedge(u, v);
addedge(v, u);
}
cnt = ;
memset(vis, , sizeof(vis));
get_timestamp();
for (int i = ; i <= n; i++)
lowbit[i] = i & (i ^ (i - ));
for (int i = ; i <= n; i++)
update(i, );
int m;
scanf("%d",&m);
while (m--)
{
char op;
int x;
getchar();
scanf("%c %d",&op,&x);
if (op == 'Q')
printf("%d\n",query(ed[x]) - query(st[x] - ));
else
{
if (query(st[x]) - query(st[x] - ) == )
update(st[x], -);
else update(st[x], );
}
}
return ;
}

POJ 3321 Apple Tree 树状数组+DFS的更多相关文章

  1. POJ 3321 Apple Tree (树状数组+dfs序)

    题目链接:http://poj.org/problem?id=3321 给你n个点,n-1条边,1为根节点.给你m条操作,C操作是将x点变反(1变0,0变1),Q操作是询问x节点以及它子树的值之和.初 ...

  2. POJ 3321 Apple Tree(树状数组)

                                                              Apple Tree Time Limit: 2000MS   Memory Lim ...

  3. POJ 3321 Apple Tree 树状数组 第一题

    第一次做树状数组,这个东西还是蛮神奇的,通过一个简单的C数组就可以表示出整个序列的值,并且可以用logN的复杂度进行改值与求和. 这道题目我根本不知道怎么和树状数组扯上的关系,刚开始我想直接按图来遍历 ...

  4. POJ--3321 Apple Tree(树状数组+dfs(序列))

    Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 22613 Accepted: 6875 Descripti ...

  5. E - Apple Tree(树状数组+DFS序)

    There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. ...

  6. 3321 Apple Tree 树状数组

    LIANJIE:http://poj.org/problem?id=3321 给你一个多叉树,每个叉和叶子节点有一颗苹果.然后给你两个操作,一个是给你C清除某节点上的苹果或者添加(此节点上有苹果则清除 ...

  7. POJ 3321:Apple Tree 树状数组

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 22131   Accepted: 6715 Descr ...

  8. POJ3321 Apple Tree(树状数组)

    先做一次dfs求得每个节点为根的子树在树状数组中编号的起始值和结束值,再树状数组做区间查询 与单点更新. #include<cstdio> #include<iostream> ...

  9. POJ 2486 Apple Tree [树状DP]

    题目:一棵树,每个结点上都有一些苹果,且相邻两个结点间的距离为1.一个人从根节点(编号为1)开始走,一共可以走k步,问最多可以吃多少苹果. 思路:这里给出数组的定义: dp[0][x][j] 为从结点 ...

随机推荐

  1. 继承Thread类使用多线程

    java实现多线程有两种方式,一种是继承Thread类,另外一种就是实现Runnable接口. 两种实现方法的优缺点: 使用Thread类实现多线程局限性就是不支持多继承,因为java是不支持类多继承 ...

  2. 网络抢票黄牛,大部分是骗人的。公布一个骗钱黄牛,QQ:2233261390,QQ群:29443597,支付页面:https://me.alipay.com/q336

    想着给女友买张回广州的回程火车票,抢了3天也没弄到.情急之下找了网络上所谓的黄牛.结果上当受骗.具体经过是这样的: 对方承诺抢到再付款.于是等他抢到后截图给我看,而且可以远程到他的机器去看,我也确实远 ...

  3. Pass Data Between ASP.NET Pages

    There is the data to send in current page <asp:TextBox ID="DataToSendTextBox" runat=&qu ...

  4. 14 Java虚拟机实现 synchronized

    java 中的 synchronized 运行 在 Java 中,我们经常用 synchronized 关键字对程序进行加锁.无论是一个代码块还是静态方法或者实例方法,都可以直接用 synchroni ...

  5. [oldboy-django][2深入django]mysql查询语句--原生sql

    # 增(一共有三种方式) # 插入单条记录 insert into t1(name,...) values('lzp',..); 注意一点:t1(name,...)必须包含所有非空列(除去自增列) # ...

  6. 关于caffe 是如何卷积的一点总结

    最近,在看caffe源码时,偶然在网上看到一个问题?觉得挺有意思,于是,仔细的查了相关资料,并将总结写在这里,供大家迷惑时,起到一点启示作用吧. 问题的题目是CNN中的一个卷积层输入64个通道的特征子 ...

  7. 【转】Unity3D 射线Ray实现点击拾取

    游戏中经常会有鼠标移动到某个对象上来拾取它的功能,我们可以用Unity3D中的射线Ray实现这一效果.原理是在我们鼠标的位置,从屏幕射出一条射向世界空间的射线,当这条射线碰撞到我们需要拾取的对象时,我 ...

  8. vNS12 MySQL Performance

    vNS12 MySQL Performance vNS12(192.168.195.91): 2vCPU + 2GBMemMySQL1(192.168.185.73): 2vCPU + 1GBMemM ...

  9. linux系统——网络调试工具

    http://blog.csdn.net/chinalinuxzend/article/details/1799279 1.网络调试工具概说: 如 果我们把一台机器接入网络中,通过网络配置工具的配置这 ...

  10. 流浪者(rover)

    流浪者(rover) 题目描述 有一位流浪者正在一个n∗mn∗m的网格图上流浪.初始时流浪者拥有SS点体力值. 流浪者会从(1,1)(1,1)走向(n,m)(n,m),并且他只会向下走((x,y)→( ...