题目
[树状数组]BZOJ 1452:开多个树状数组解决问题
[树状数组, 并查集]BZOJ 3211:并查集辅助树状数组求值
模板及讲解
知识点:
1. 点修改,求x~y区间值
2. 区间修改,求某一点值
3. 二维树状数组
4. 离散化求逆序对
1 点修改,求x~y区间值
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define ms(i,j) memset(i,j, sizeof i);
using namespace std;
int a[500005];
int n,m;
int abss(int x){return x>=0 ? x : -x;}
int lowbit(int x)
{
return x&(-x);
}
int getsum(int x)//求1~x的和
{
int ret = 0;
for (int i=x;i>0;i-=lowbit(i))
{
ret += a[i];
}
return ret;
}
void addsum(int x, int y)//1~x加y
{
for (int i=x;i<=n;i+=lowbit(i))
{
a[i] += y;
}
}
int main()
{
a[0] = 0;
scanf("%d%d", &n, &m);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d", &x);
addsum(i,x);
}
for (int i=1;i<=m;i++)
{
int ty;
scanf("%d", &ty);
if (ty==1)
{
int x,k;
scanf("%d%d", &x, &k);
addsum(x,k);
} else
{
int x,y;
scanf("%d%d", &x, &y);
printf("%d\n", abss(getsum(y)-getsum(x-1)));
}
}
return 0;
}
2 区间修改,求某一点值
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ms(i,j) memset(i,j,sizeof i);
int n,m;
const int maxn = 500005;
int a[maxn];//a记录的是比i-lowbit(i)多的值
int lowbit(int x)
{
return x&(-x);
}
int add(int x, int v)
{
for (int i=x;i<=n;i+=lowbit(i))
{
a[i] += v;
}
}
int sub(int x)
{
int ret = 0;
for (int i=x;i>0;i-=lowbit(i))
{
ret += a[i];
}
return ret;
}
int main()
{
scanf("%d%d", &n ,&m);
ms(a,0);
for (int i=1;i<=n;i++)
{
int x;
scanf("%d", &x);
add(i,x);
add(i+1,-x);
}
for (int i=1;i<=m;i++)
{
int ty;
scanf("%d", &ty);
if(ty==1)
{
int x,y,k;
scanf("%d%d%d", &x,&y,&k);
add(x,k); add(y+1,-k);
} else
{
int x;
scanf("%d", &x);
printf("%d\n", sub(x));
}
}
system("pause");
return 0;
}
3 二维树状数组
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ms(i,j) memset(i, j, sizeof i);
int a[1050][1050];
int n;
int lowbit(int x)
{
return x&(-x);
}
void addsum(int x, int y, int v)//得到和
{
for (int i=x;i<=n;i+=lowbit(i))
{
for (int j=y;j<=n;j+=lowbit(j))
{
a[i][j] += v;
}
}
}
int getsum(int x, int y)//加
{
int ret = 0;
for (int i=x;i>0;i-=lowbit(i))
{
for (int j=y;j>0;j-=lowbit(j))
{
ret += a[i][j];
}
}
return ret;
}
int main()
{
scanf("%d", &n);
int m;
while(scanf("%d", &m)==1&&m!=3)
{
if (m==1)
{
int x,y,k;
scanf("%d%d%d", &x, &y, &k);
addsum(x+1,y+1,k);
} else
{
int x1,y1,x2,y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", getsum(x2+1,y2+1)-getsum(x1,y2+1)-getsum(x2+1,y1)+getsum(x1,y1));
//公式,因为防止0所以都+1
}
}
system("pause");
return 0;
}
4 离散化求逆序对
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ms(i,j) memset(i, j, sizeof i);
struct node
{
int v;
int num;
bool operator<(const node &a) const
{
return v<a.v;
}
}t[40005];//输入数
int hash[40005];//映射表
int a[40005];//树状数组
int n;
int ans = 0;
int lowbit(int x)
{
return x&(-x);
}
int getsum(int x)//取值
{
int ret = 0;
for (int i=x;i>0;i-=lowbit(i))
{
ret += a[i];
}
return ret;
}
int addsum(int x, int v)//加值
{
for (int i=x;i<=n;i+=lowbit(i))
{
a[i] += v;
}
}
int main()
{
scanf("%d", &n);
for (int i=1;i<=n;i++)
{
scanf("%d", &t[i].v);
t[i].num = i;
}
sort(t+1,t+1+n);//排序
for (int i=1;i<=n;i++)
{
hash[t[i].num] = i;//离散化
}
for (int i=1;i<=n;i++)
{
addsum(hash[i], 1);
ans += i-getsum(hash[i]);
//getsum(hash[i]): 有多少个数在hash[i]右边
//i 已经插了几个数
}
printf("%d\n",ans);
system("pause");
return 0;
}