1. 程式人生 > >poj apple tree 樹狀陣列

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;
}