树状数组我感觉是真的很神奇,不知道谁被上帝敲了脑门想出来了鬼才办法
虽然自己也知道大的数据可以二分,或者尝试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;
}