Problem Description
N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?
Input
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。
当N = 0,输入结束。
Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。
Sample Input
3 1 1 2 2 3 3 3 1 1 1 2 1 3 0
Sample Output
1 1 1 3 2 1
题解:如果区间[a,b]都加上1,那么我将a这点加上1,求[a,b]区间任意一个时,我对前面数组求和不就得到了结果吗?不用每次都加。树状数组就能解决求区间和问题。
树状数组:
#include <iostream>
#include <cstring>
#include <cstdio>
#define mem(a) memset(a,0,sizeof(a));
using namespace std;
int a[100005];
void update(int x,int n,int plus) //更新数组
{
while(x <= n)
{
a[x] += plus;
x += x & (-x);
}
}
int getSum(int n)
{
int res = 0;
while(n > 0)
{
res += a[n];
n -= n & (-n);
}
return res;
}
int main()
{
int n;
while(scanf("%d",&n) && n != 0)
{
mem(a);
for(int i = 0;i < n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
update(l,n,1); //将l这个点加1
update(r + 1,n,-1); //求和时,r后面的需要减1,因为这些点不在要求修改的区间
}
for(int i = 1;i < n;i++)
{
printf("%d ",getSum(i));
}
printf("%d\n",getSum(n));
}
return 0;
}
平常做法:
#include <iostream>
#include <cstring>
#include <cstdio>
#define mem(a) memset(a,0,sizeof(a));
using namespace std;
int a[100005];
int main()
{
int n;
while(scanf("%d",&n) && n != 0)
{
mem(a);
for(int i = 0;i < n;i++)
{
int l,r;
scanf("%d%d",&l,&r);
a[l] += 1; //l这里加1
a[r + 1] -= 1; //r+1这里减1,以后再求前i项的和就得到了单点值
}
int res = 0;
for(int i = 1;i < n;i++)
{
a[i] += a[i - 1];
printf("%d ",a[i]);
}
printf("%d\n",a[n] + a[n - 1]);
}
return 0;
}
线段树区间修改:其实就是减少递归次数。因为我最后(包括查询)肯定会访问到每一个节点。所以我可以这个时候更新孩子。
#include <iostream>
#include <cstdio>
#include <cstring>
#define mem(a) memset(a,0,sizeof(a));
using namespace std;
struct Node
{
int l;
int r;
int sum;
};
Node a[500005];
int add[500005];
void segTree(int k,int l,int r) //线段树的建立
{
a[k].l = l; //区间信息
a[k].r = r;
a[k].sum = 0;
if(l == r)
{
return;
}
int mid = (l + r) >> 1;
segTree(k << 1,l,mid);
segTree((k << 1) | 1,mid + 1,r);
}
void pushDown(int k,int m) //向下更新
{
if(add[k] != 0) //该区间有更新的需要
{
a[k << 1].sum += add[k] * (m - (m >> 1)); //左孩子更新
a[(k << 1) | 1].sum += add[k] * (m >> 1);
add[k << 1] += add[k]; //左孩子标记更新
add[(k << 1) | 1] += add[k];
add[k] = 0; //该区间已经向下更新了
}
}
void pushUp(int k) //向上更新
{
a[k].sum = a[k << 1].sum + a[(k << 1) | 1].sum;
}
void update(int k,int l,int r,int plus)
{
if(a[k].l == l && a[k].r == r) //该区间找到,结束,等下次找到左右孩子才更新
{
add[k] += plus;
a[k].sum += (r - l + 1) * plus;
return;
}
pushDown(k,a[k].r - a[k].l + 1); //将该点孩子更新,因为要访问孩子了
int mid = (a[k].l + a[k].r) >> 1;
if(l > mid)
{
update((k << 1) | 1,l,r,plus);
}
else if(r <= mid)
{
update(k << 1,l,r,plus);
}
else
{
update(k << 1,l,mid,plus);
update((k << 1) | 1,mid + 1,r,plus);
}
pushUp(k); //向上更新
}
void query(int k,int n)
{
pushDown(k,a[k].r - a[k].l + 1);
if(a[k].l == a[k].r)
{
if(a[k].l == n)
{
printf("%d\n",a[k].sum);
}
else
{
printf("%d ",a[k].sum);
}
return;
}
query(k << 1,n);
query((k << 1) | 1,n);
}
int main()
{
int n;
while(scanf("%d",&n) && n != 0)
{
mem(a);
mem(add);
segTree(1,1,n);
int l,r;
for(int i = 0;i < n;i++)
{
scanf("%d%d",&l,&r);
update(1,l,r,1);
}
query(1,n);
}
return 0;
}