集训的时候学的树状数组,写个博客记录一下,下面理论是讲课的pdf
挺清楚的.
记录两个例题
1、Apple Tree
这个题算是线状数组和线段树的板子,但是处理的时候要把点都转化为区间,这时候就要用dfs去后序遍历树了,即把第一次到该节点的值当作该点区间的左边界,第二次回溯到该节点时的值当作区间右边界
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
//边表为 edge,其中第 i 条边相连的节点为 edge[i].tail;连接的
//下条边的序号为 edge[i].next
struct node1
{
int next,tail;
}edge[maxn];
//苹果树为 apple,以节点 i 为根的子树在后序序列中的区间为[apple[i].l,apple[i].r]
struct node2
{
int l,r;
}apple[maxn];
int s[maxn],c[maxn],a[maxn],cnt;
//后序遍历中第 i 个节点的权值为 a[i];后序遍历序号为 cnt;
//树状数组为 c;节点 i 相连的第 i 条边的序号为 s[i]
//后序遍历构造区间,计算每个节点的后序值
void DFS(int u)
{
apple[u].l = cnt;
for(int i = s[u]; i != -1; i = edge[i].next){
DFS(edge[i].tail);
}
apple[u].r = cnt++;
}
int lowbit(int x)
{
return x&(-x);
}
void change(int x)
{
if(a[x]){
for(int i = x; i < cnt; i += lowbit(i)){
c[i]++;
}
}
else{
for(int i = x; i < cnt; i += lowbit(i)){
c[i]--;
}
}
}
int sum(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += c[i];
}
return res;
}
int main()
{
int n,m;
scanf("%d",&n);
memset(s,-1,sizeof(s));
for(int i = 0; i < n - 1; i++){
int t1,t2;
scanf("%d%d",&t1,&t2);
//链式前向星
edge[i].tail = t2;
edge[i].next = s[t1];
s[t1] = i;
}
cnt = 1;
DFS(1);
scanf("%d",&m);
for(int i = 1; i <= n; i++){
a[i] = 1;
change(i);
}
while(m--){
char str[2];
int t;
scanf("%s%d",str,&t);
if(str[0] == 'Q'){
int ans = sum(apple[t].r) - sum(apple[t].l - 1);
printf("%d\n",ans);
}
else{
if(a[apple[t].r]){
a[apple[t].r] = 0;
}
else{
a[apple[t].r] = 1;
}
change(apple[t].r);
}
}
}
2、利用树状数组求逆序对
题目链接Japan
具体一些详细的解释在代码里
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1005;
struct node//连接高速路两端的城市序号
{
int x,y;
}edge[maxn * maxn];//高速路序列
ll c[maxn];
int n,m;//n 和 m 为东海岸与西海岸的城市数
bool cmp(node a,node b)
{
if(a.x == b.x){
return a.y <= b.y;
}
return a.x < b.x;
}
int lowbit(int x)
{
return x&(-x);
}
void Modify(int x,int v)
{
for(int i = x; i <= m; i += lowbit(i)){
c[i] += v;
}
}
ll sum(int x)
{
ll res = 0;
for(int i = x; i > 0; i -= lowbit(i)){
res += c[i];
}
return res;
}
int main()
{
int cnt = 0,t;
scanf("%d",&t);
while(t--){
int k;
scanf("%d%d%d",&n,&m,&k);
for(int i = 0; i < k; i++){
scanf("%d%d",&edge[i].x,&edge[i].y);
}
memset(c,0,sizeof(c));
sort(edge,edge + k,cmp);
ll ans = 0;
for(int i = 0; i < k; i++){
//表示记录并更新当前元素插入后树状数组内个数
Modify(edge[i].y,1);
//如果插入这个元素后,用当前插入的数的总数
//减去小于等于该数的数的数量,那么剩下的就是在
//插入该数之前插入的大于该数的数字,那么这个数量就是
//该数的逆序对的数量
ans += sum(m) - sum(edge[i].y);
//把所有数的逆序对数量相加,就是整个数列内的逆序对数量
}
cnt++;
printf("Test case %d: %lld\n",cnt,ans);
}
return 0;
}