这道题本来没什么好写的,直接树状数组标记一下就能解了。
有个学弟说这道题用线段树怎么写都是MLT, 大概想了一下,对于这道题的话,线段树的内存还是能砍掉许多的。
PS:这道题的正解显然不是线段树,本文只是从"如果要用线段树的话怎么办"的角度去写,小朋友们切勿模仿。
对于树状数组:
如果要对区间[x, y]整体加上1的话,在树状数组中,让num[x]++, num[y+1]-- 即可。
树状数组在update的时候已经在求和了,所以在查询的时候,直接输出getsum(x)的值即可。
对于线段树:
对于这道题而言,每次都是在区间上加上1,因此可以预计到最终询问的时候这个值不会太大(就算某区间加了一百万次,值也只有一百万),而int的范围有42亿,可以看出这个空间太浪费了。所以可以考虑在一个int变量里存两个值。假设能成功的话,那么原本需要存1~n这样n个点的空间,现在只需要存n/2个点的空间即可。
举个例子:一共有10个点,1~10,每次给两种操作,一是区间[x, y]整体+1,二是查询某个点的值。
原本的存储空间为:
为了在一个Int内放两个数字,假设要在位置x上+t,需要这样操作:
如果x <= 5,那么num[x] += t;如果x > 5,那么num[x-5] += t * 10000。
这里的10000作为两个数字的分隔点,就好比一个大柜子中间用隔板分成两个小柜子。上层的"柜子"以一万的整数倍进行存储,下层的"柜子"按照原数字存储。
需要注意的问题是,必须保证上层和下层的"柜子"都不会溢出,本题显然无压力。
这样的话需要开辟的空间就少了一半了。
这样存好以后怎么用起来呢?
当查询的点x <= 5的时候,输出num[x] % 10000
当查询的点x > 5的时候,输出num[x] / 10000
至此成功实现了数据量缩小一半的任务。其实还能够实现空间缩小4倍,8倍之类的功能,只要保证每一个隔开的"小柜子"都不会满出来就行了。
大致写个个程序,供参考:
#include<iostream>
using namespace std;
#define maxn 100
int tree[maxn];
int num[maxn];
int part;
void build(int l, int r, int root)
{
tree[root] = 0;
if(l == r)
{
return;
}
int mid = (l + r)>>1;
build(l, mid, root<<1);
build(mid+1, r, root<<1|1);
}
void down(int l, int r, int root) // 标记下传
{
int mid = (l + r) >> 1;
tree[root<<1] += (mid - l + 1) * num[root];
num[root<<1] += num[root];
tree[root<<1|1] += (r - mid) * num[root];
num[root<<1|1] += num[root];
num[root] = 0;
}
void update(int l, int r, int s, int t, int val, int root)
{
if(s <= l && t >= r)
{
tree[root] += val*(r - l + 1);
num[root] += val;
return;
}
if(num[root])
down(l, r, root);
int mid = (l + r) >> 1;
if(s <= mid)
update(l, mid, s, t, val, root<<1);
if(t > mid)
update(mid+1, r, s, t, val, root<<1|1);
tree[root] = tree[root<<1] + tree[root<<1|1];
}
int query(int l, int r, int pos, int root)
{
if(l == r)
{
return tree[root];
}
if(num[root])
down(l, r, root);
int mid = (l + r) >> 1;
int re1 = 0, re2 = 0;
if(pos <= mid)
return query(l, mid, pos, root<<1);
else
return query(mid+1, r, pos, root<<1|1);
}
int main()
{
int n, m;
int a, b;
part = 5; // 一共10个点,以5为分隔
while(~scanf("%d", &n))
{
build(1, part, 1);
for(int i = 0; i < n; i++)
{
scanf("%d%d", &a, &b);
if(b <= part) // 区间只在下层
{
update(1, part, a, b, 1, 1);
}
else if(a > part) // 区间只在上层
update(1, part, a-part, b-part, 1*1000, 1);
else // 两层都要更新
{
update(1, part, a, part, 1, 1);
update(1, part, 1, b-part, 1*1000, 1);
}
}
scanf("%d", &m);
for(int i = 0; i < m; i++)
{
scanf("%d", &a);
int re = 0;
if(a <= part)
{
re = query(1, part, a, 1);
printf("re = %d\n", re%1000); // 过滤掉上层的数据,保留下层的
}
else
{
re = query(1, part, a-part, 1);
printf("re = %d\n", re/1000); // 过滤掉下层的数据,保留上层的
}
}
}
return 0;
}