poj3321解题报告
来源:互联网 发布:华三交换机端口状态 编辑:程序博客网 时间:2024/06/14 05:19
给你一颗苹果树,树的主干设为1,每一个分支设为一个数,一直到N,代表这颗苹果树。每个分支上面只能最多有一个苹果,也就是一个枝子上面不可能有两个苹果,另外注意一点,不要把苹果树想象成二叉树,苹果树每个节点可以分出很多叉,应该是多叉树。
输入是叉之间的关系,
1 2
1 3
就是主干上面两个叉分别是2 和3.
下面是两种操作,Q 和C
C j 的意思是如果 j 这个枝子上面有苹果就摘下来,如果没有,那么就会长出新的一个
Q j 就是问 j 这个叉上面的苹果总数。
这个题很明显是线段树,其实我很多时候就用树状数组解决,但是,这个数组怎么来定?????算法很容易搞定,但是初始化是问题。
先来说一下树状数组:
先看一个例题:
数列操作:
给定一个初始值都为0的序列,动态地修改一些位置上的数字,加上一个数,减去一个数,或者乘上一个数,然后动态地提出问题,问题的形式是求出一段数字的和.
若要维护的序列范围是0..5,先构造下面的一棵线段树:
可以看出,这棵树的构造用二分便可以实现.复杂度是2*N.
每个结点用数组a来表示该结点所表示范围内的数据之和.
修改一个位置上数字的值,就是修改一个叶子结点的值,而当程序由叶子结点返回根节点的同时顺便修改掉路径上的结点的a数组的值.
对于询问的回答,可以直接查找i..j范围内的值,遇到分叉时就兵分两路,最后在合起来.也可以先找出0..i-1的值和0..j的值,两个值减一减就行了.后者的实际操作次数比前者小一些.
这样修改与维护的复杂度是logN.询问的复杂度也是logN,对于M次询问,复杂度是MlogN.
然而不难发现,线段树的编程复杂度大,空间复杂度大,时间效率也不高.
更好的解决办法就是用树状数组!
下图中的C数组就是树状数组,a数组是原数组
对于序列a,我们设一个数组C定义C[i] = a[i-2^k+1] + a[i-2^k+2] + … + a[i],k为i在二进制下末尾0的个数。
2^k的计算可以这样:2^k = i & (i^(i-1))
- inline int Lowbit(int x){
- // 求x最低位1的权
- return x & (x^(x-1));
- }
- 定义C[i] = a[i-2^k+1]+a[i-2^k+2]+...+a[i];
- 若要修改 a[k]的值,则C数组的修改过程如下:(n为C数组的大小)
- void Change(int k, int delta){
- while( k<=n ){
- C[k] += delta;
- k += LowBit(k);
- }
- }
- 求a中1..k元素的和
- int Getsum(int k){
- int ret = 0;
- while( k>0 ){
- ret += C[k];
- k -= Lowbit(k);
- }
- return ret;
- }
若要求i..j的元素和的值,则求出 1~i-1和1~j的值,相减即可
在很多的情况下,线段树都可以用树状数组实现.凡是能用树状数组的一定能用线段树.当题目不满足减法原则的时候,就只能用线段树,不能用树状数组.例如数列操作如果让我们求出一段数字中最大或者最小的数字,就不能用树状数组了.
注:用数组保存静态一维线段数也可以不记录左右孩子的下标
如0号是根节点,其孩子是1、2;
1号的孩子是3、4;
2号的孩子是5、6;
3号的孩子是7、8;
……
n号的孩子是2*n+1、2*n+2。
再说说本题如何用树状数组来解决:
想必大家都明白上面说的了吧,就是为了快速的查询一个数组某一段的和,一旦这一段经过修改,依然快速给出答案。
而这个题不正是这个意思么,原先都是一个苹果,摘掉或者新长出,不就是改变数组的某个值么,这时候要查询的不就是某个区间的值么?
而上面的例子就是一个一维数组,但是现在如何把一个树转化为一维数组呢?这是这道题目的关键,否则没法做。
如果理解了这一点,就好办了。
对一棵树进行深搜,然后将深搜的顺序重新标上号,然后放到一个数组中,记下每一个枝子得起始点和终结点,然后就可以用树状数组了。
还是举个例子吧。。。
如果一棵苹果树是这样的。
那么我们知道1是主干。。。。
深搜的顺序是1 4 6 5 7 2 3 当然这也是根据题目的输入建的广义表。
那么当我们深搜的时候就可以在下面标上:
1 4 6 5 7 2 3 (广义表里面的东西,深搜结果)
1 2 3 4 5 6 7(num数组我们求和要用的)
这样不就是树状数组了么。
我们用start【1】= 1 end【1】=7代表1以上的树枝为1-7,也就是所有
用 start【2】= 6 end【2】 = 7 代表2以上的树枝是 3
同理
start【4】=2 end【4】 = 5 这样我们就知道4上面的树枝了
每一个树枝在num【n】总数组中的位置,这样我们就可以计算啦。。。树状数组,和上面将的一样了,成功转换
具体键代码,main函数主要建广义表,dfs主要是找到每一个树枝在num【n】数组中的起点和终点并记录在start和end数组中。
这样下面就是树状数组的三个函数。
有人会问,为什么要+ 或者- x & (x^(x-1)),这个我们可以看上面的规律,具体起源于什么,我也不知道,根据上面讲的就是这个数的二进制从右边第一个1的位置。
如果还不是很明白,请回复,我会的尽量解释,谢谢。
代码:
- #include <iostream>
- #include <cstdio>
- using namespace std;
- int n, m;
- int inc = 0;
- int num[100001];
- int start[100001];
- int end[100001];
- //要用到的广义表的结构
- struct TREE
- {
- int data;
- TREE * next;
- };
- TREE tree[100001];
- void dfs(int pos)
- {
- int i, j, k;
- start[pos] = ++ inc;
- TREE *p = tree[pos].next;
- while (p)
- {
- if (start[p->data] == 0)
- {
- dfs(p->data);
- }
- p = p->next;
- }
- end[pos] = inc;
- }
- //中间用到的规律值
- int lowBit(int x)
- {
- return x&(x^(x-1));
- }
- //求从开始到这里的和
- int sSum(int end)
- {
- int sum = 0;
- while (end > 0)
- {
- sum += num[end];
- end -= lowBit(end);
- }
- return sum;
- }
- //更新自己并且和自己相关的
- void change(int pos, int tmp)
- {
- while (pos <= n)
- {
- num[pos] += tmp;
- pos += lowBit(pos);
- }
- }
- int main()
- {
- int j,k,s,t;
- scanf("%d", &n);
- for (int i = 1; i < n; i ++)
- {
- //每一个点都建一个长长的链表,表示自己的一个分支
- scanf("%d%d", &s, &t);
- TREE *p = new TREE;
- p->data = t;
- p->next = tree[s].next;
- tree[s].next = p;
- TREE *q = new TREE;
- q->data = s;
- q->next = tree[t].next;
- tree[t].next = q;
- }
- //映射到树状数组
- dfs(1);
- //每个初始化有一个苹果
- for (int i = 1; i <= n; i ++)
- {
- change(i, 1);
- }
- char ch;
- scanf("%d", &m);
- for (int i = 0; i < m; i ++)
- {
- getchar();
- scanf("%c%d", &ch, &j);
- if (ch == 'C')
- {
- int sum = sSum(start[j]);
- sum -= sSum(start[j] - 1);
- if (sum == 1)
- {
- change(start[j], -1);
- }
- else
- {
- change(start[j], 1);
- }
- }
- else
- {
- int sum = sSum(end[j]);
- sum -= sSum(start[j] - 1);
- printf("%d\n", sum);
- }
- }
- return 0;
- }
- poj3321解题报告
- poj3321
- poj3321
- poj3321
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- 解题报告
- Antiprime解题报告
- expr解题报告
- 华容道解题报告
- tju解题报告
- zju1062/pku1095解题报告
- UsacoGate解题报告 --- 序曲
- ZJU 2060 解题报告
- 懒加载三大优势
- krpano 教程 - 雪花效果插件的使用
- ICON ios 7
- Morris 树遍历算法
- Hadoop
- poj3321解题报告
- POJ 1007 Difference Between Primes(线性筛法求N以内的素数表)
- JAVA Dynamic Proxy
- 关于Android创建Activity需要注意的地方
- jquery easyui datagrid 列自适应窗口宽度
- 快捷键让SublimeText在编文件快速在浏览器打开
- 二十三:微信公众帐号开发-图文消息全攻略
- objective c的分类
- Linux中断处理流程