1.题目引入:
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,输入结束。Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。
2.样例输出:
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
显然本题是区间修改单点查询,这里引入树状数组,和 sum 数组相似是一个记录和的数组,只不过他存放的不是前 i 个整数之和,而是在 i 号为之前(含 i 号位)lowbit(i) 个整数之和。
本题是区间覆盖 ,故每个 i 都有其覆盖的长度它是 2 的幂次。
此处强调,树状数组的定义非常重要,特别是 “t[i]的覆盖长度是 lowbit(i)”这一点:另外树状数组的下标必须从 1 开始。
3.代码如下:
#include<iostream> #include<algorithm> using namespace std; const int maxn=1e5+10; int t[maxn],n; #define lowbit(i) ((i)&(-i)) void update(int x,int v) { for(int i=x;i;i-=lowbit(i)) { t[i]+=v; //修改节点的值 } } int sum(int x) { int ans=0; for(int i=x;i<=n;i+=lowbit(i)) { ans+=t[i]; } return ans; } int main() { int a,b; while(cin>>n&&n) { fill(t,t+n+1,0); for(int i=0;i<n;i++) { cin>>a>>b; //本题是倒着进行,当让顺着进行也可以 update(b,1); //小于 b 的节点值都进行加一 update(a-1,-1); //小于最小边界后需要恢复 //这其实是由大到小覆盖的过程 } for(int i=1;i<n;i++) { cout<<sum(i)<<" "; //只需要单个访问就可以 } cout<<sum(n)<<endl; } return 0; }
2.代码如下:
#include<iostream> #include<algorithm> using namespace std; const int maxn = 1e5+01; int t[maxn]; int main() { int N; while(cin>>N&&N) { fill(t,t+N+1, 0); for(int i = 1; i <= N; i++) { int a, b; cin>>a>>b; t[a]++; //其实是从小到大进行节点赋值以实现区间赋值 t[b+1]--; } for(int i=1;i<N;i++) { t[i]+=t[i-1]; //这里运用了记忆化搜索的思想 cout<<t[i]<<" "; } cout<<t[N]+t[N-1]<<endl; } return 0; } 1 2 3 1 -1 0 2 -1 -1 3 -1 -1 : 3 2 1