题意:一棵苹果树有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. Java中Scanner中nextLine()方法和next()方法的区别

    https://blog.csdn.net/hello_word2/article/details/54895106

  2. 微服务学习笔记——Spring Boot特性

    1. 创建独立的Spring应用程序 2. 嵌入的Tomcat,无需部署WAR文件 3. 简化Maven配置 4. 自动配置Spring 5. 提供生产就绪型功能,如指标,健康检查和外部配置 6. 开 ...

  3. jeakins配置邮件通知,附带解决535报错:authentication failed,如果发现测试邮件可以发出,项目构成无法发出邮件,请开启SSL认证,端口号改为(465),qq邮箱、163邮箱通用

    535报错解决方案:调用163邮箱服务器来发送邮件,我们需要开启POP3/SMTP服务,这时163邮件会让我们设置客户端授权码,这个授权码替代上面代码部分的passwd即可成功发送邮件 如果设置的邮箱 ...

  4. 更改 Mac 上的功能键行为

    您可以将 Apple 键盘上的顶行按键用作标准功能键,或用来控制 Mac 的内建功能.   如果您的 Apple 键盘部分顶行按键上印有图标,则这些按键可用于执行每个图标所示的特殊功能.这些按键也可用 ...

  5. HDU5862 Counting Intersections

    Given some segments which are paralleled to the coordinate axis. You need to count the number of the ...

  6. log4net实现多实例记录

    原文地址:实现多个LOG4NET日志记录器实例 本文内容为摘抄,请查看原文. 对于.NET Framework开发者来说,使用Log4Net进行日志记录是非常方便的,通常只要写好配置文件和简单的编码就 ...

  7. C#从大图中截取一部分图片

    #region 从大图中截取一部分图片 /// <summary> /// 从大图中截取一部分图片 /// </summary> /// <param name=&quo ...

  8. maven国内镜像

    <?xml version="1.0" encoding="UTF-8"?> <!--Licensed to the Apache Softw ...

  9. HEAD DETACHED push origin失败问题

    先说HEAD HEAD是一个头指针,通常情况下指向不同的分支,每个分支对应一个commit(准确的说,每个分支对应多个commit,但是只有一个顶层的commit,而commit之间是简单的线性关系. ...

  10. centos安装arm交叉工具链后常见的问题解决

    [root@localhost osdrv]# arm-hisiv400-linux-gcc -vbash: /opt/hisi-linux/x86-arm/arm-hisiv400-linux/ta ...