poj apple tree 树状数组

树状数组我感觉是真的很神奇,不知道谁被上帝敲了脑门想出来了鬼才办法

虽然自己也知道大的数据可以二分,或者尝试log(n)

但是不得不说...真的厉害

树状数组:适用于区间求和,单点爆破更新,要用到lowbit,即最低位

lowbit(x)= x & (x^(x-1))  = x &(-x) :因为负数补码是正数取反之后加一的,在x中从右向左,只要有出现一个非0位(非0位之前都是0)那~x在这一位之前都是1,到这是0

加一之后相当于把一串一左移,然后第一个x与~x+1(~x+1=-x)的一相遇的地方就是首位

例如:x=001000  ~x=110111 ~x+1=111110(看作是最前面的1左移了)就能找到lowbit

树状数组C[i] = a[i-lowbit(i)+1] + …+ a[i] ,其中i从一开始lowbit(i)>=1

对求和操作sum(k)= a[1]+a[2]+…+a[k]
sum(k) = C[n(1)]+C[n(2)] + …+ C[n(m)]   n(m)= k   ,    n(i-1) =n(i) - lowbit(n(i)) 用了括号并不是因为不会写脚标

更新的话要修改的仅有:C[n(1)], C[n(2)],  …C[n(m)]

其中,n(1) = i ,n(p+1) = n(p)+ lowbit(n(p))

中间有一大堆的数学证明,很厉害就是了...刚刚好

求和时间是log(n)的,因为每一次取lowbit(i)的话,最多右log(i)+1位个1(二进制嘛~)当然在更新和求数组上费点事情

本题比模板要麻烦很多

大意是更新更新更新之后查询一个树枝上的苹果数目

输入

The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.
The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.
The next line contains an integer M (M ≤ 100,000).
The following M lines each contain a message which is either
"C x" which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.
or
"Q x" which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x
Note the tree is full of apples at the beginning

输出

For every inquiry, output the correspond answer per line.

样例输入

3
1 2
1 3
3
Q 1
C 2
Q 1

样例输出

3
2

思路:开始想并查集的,但感觉不行(而且是树状数组方面的题)先用dfs,给每个节点标号,但我写了两次加1不然会很麻烦...(因为dfs进出刚好仅遍历所有的这个树枝上的结点)求一个树枝上的所有苹果只需要算来遍历之前的和和遍历之后的和相减就好了。但是写起来很难...

 

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int lowbit(int x)
{
	return x & (-x);
}
int c[200002] = {};//树状数组
int num[100001] = {};//每个枝上的苹果数目
int start_t[100001], end_t[100001];
bool vis[100001] = {};
int t = 0;
vector<int>rela[100001];//和每个点邻接的点数
typedef vector<int>::iterator loop;
void dfs(int x)//深搜获得标号
{
	t++;
	start_t[x] = t;
	vis[x] = true;
	for (loop i = rela[x].begin(); i != rela[x].end(); i++)
	{
		if (!vis[*i])
		{
			dfs(*i);
		}
	}
	t++;
	end_t[x] = t;
}//加了两次1
int main()
{
	int m, n;
	char sign;
	int x;
	cin >> n;
	for (int i = 0; i <= 100001; i++)
	{
		num[i] = 1;
	}
	for (int i = 0; i <= 2 * n; i++)
	{
		c[i] = lowbit(i);//树状数组,因为开始都是1
	}
	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		rela[a].push_back(b);//向上的邻接点
	}
	vis[1] = true;
	dfs(1);//从根节点开始深搜biaohao
	cin >> m;
	for (int i = 1; i <= m; i++)
	{
		cin >> sign >> x;
		if (sign == 'Q')//查
		{
			int tmp1 = start_t[x] - 1;
			int tmp2 = end_t[x];//开始和结束的编号
			int sumb=0, sume=0;//开始结束
			while (tmp1)
			{
				sumb += c[tmp1];
				tmp1 -=lowbit(tmp1);
			}
			while (tmp2)
			{
				sume += c[tmp2];
				tmp2 -= lowbit(tmp2);
			}
			cout << (sume - sumb)/2 << endl;
		}
		else//改变
		{
			int sub = 1 - 2 * num[x];//改变的值
			num[x] = 1-num[x];
			int tmp1 = start_t[x];
			int tmp2 = end_t[x];//修改
			while (tmp1 <=  n*2)
			{
				c[tmp1] = c[tmp1] + sub;
				tmp1 = tmp1 + lowbit(tmp1);
			}
			while (tmp2 <= n*2)
			{
				c[tmp2] = c[tmp2] + sub;//多修改了
				tmp2 = tmp2 + lowbit(tmp2);
			}
		}
	}
	return 0;
}

 

POJ 2182是一道使用树状数组解决的题目,题目要求对给定的n个数进行排序,并且输出每个数在排序后的相对位置。树状数组是一种用来高效处理前缀和问题的数据结构。 根据引用中的描述,我们可以通过遍历数组a,对于每个元素a[i],可以使用二分查找找到a到a[i-1]中小于a[i]的数的个数。这个个数就是它在排序后的相对位置。 代码中的query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。 最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。 参考代码如下: ```C++ #include <iostream> #include <cstdio> using namespace std; int n, a += y; } } int main() { scanf("%d", &n); f = 1; for (int i = 2; i <= n; i++) { scanf("%d", &a[i]); f[i = i & -i; } for (int i = n; i >= 1; i--) { int l = 1, r = n; while (l <= r) { int mid = (l + r) / 2; int k = query(mid - 1); if (a[i > k) { l = mid + 1; } else if (a[i < k) { r = mid - 1; } else { while (b[mid]) { mid++; } ans[i = mid; b[mid = true; add(mid, -1); break; } } } for (int i = 1; i <= n; i++) { printf("%d\n", ans[i]); } return 0; } ``` 这段代码使用了树状数组来完成题目要求的排序功能,其中query函数用来求前缀和,add函数用来更新树状数组。在主函数中,我们从后往前遍历数组a,通过二分查找找到每个元素在排序后的相对位置,并将结果存入ans数组中。最后,我们按顺序输出ans数组的元素即可得到排序后的相对位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [poj2182Lost Cows——树状数组快速查找](https://blog.csdn.net/aodan5477/article/details/102045839)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [poj_2182 线段树/树状数组](https://blog.csdn.net/weixin_34138139/article/details/86389799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值