Color the ball

来源:互联网 发布:nba新秀数据库 编辑:程序博客网 时间:2024/06/05 19:40
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个气球总共被涂色的次数。
Sample Input
31 12 23 331 11 21 30
Sample Output
1 1 13 2 1

一、线段树

这道题,我犯了两个错

1.一直认为更新的就是1,忘记了lazy标记点的tag,可能已经是好几个1的加和,所以我们还得在设置一个变量val,来传递参数

2.就是释放标记的时候,应该释放的是树的左中,中右,自己写成了我们传递区间的左中 中右

#include <iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define INF 0x3f3f3f3fusing namespace std;typedef long long ll;const int maxn=1e5+10;struct node{    int l,r;    int lazy,tag;}tree[maxn*4];void build(int st,int ed,int now_id){    tree[now_id].l=st;    tree[now_id].r=ed;    tree[now_id].lazy=0,tree[now_id].tag=0;    if(st==ed)        return;    int mid=(st+ed)>>1;    build(st,mid,now_id<<1);    build(mid+1,ed,now_id<<1|1);}void update(int st,int ed,int now_id,int val)//前面的加和是1,可是到后面的堆积就会不是1了{    if(tree[now_id].l==st&&tree[now_id].r==ed)//如果这就是我们的区间,那我们就停在这里,把值堆在这里    {        tree[now_id].lazy=1;        tree[now_id].tag+=val;        return;//记住写return,要不然就回不来了    }    int mid=(tree[now_id].l+tree[now_id].r)>>1;    if(tree[now_id].lazy)    {        tree[now_id].lazy=0;//记录有没有        update(tree[now_id].l,mid,now_id<<1,tree[now_id].tag);//更新的是原树        update(mid+1,tree[now_id].r,now_id<<1|1,tree[now_id].tag);        tree[now_id].tag=0;//记录是多少    }    if(ed<=mid)        update(st,ed,now_id<<1,val);    else if(st>mid)        update(st,ed,now_id<<1|1,val);    else        update(st,mid,now_id<<1,val),update(mid+1,ed,now_id<<1|1,val);}void query(int num,int now_id){    if(tree[now_id].l==tree[now_id].r)    {         printf("%d",tree[now_id].tag);         return;    }    int mid=(tree[now_id].l+tree[now_id].r)>>1;    if(tree[now_id].lazy)    {        tree[now_id].lazy=0;        update(tree[now_id].l,mid,now_id<<1,tree[now_id].tag);        update(mid+1,tree[now_id].r,now_id<<1|1,tree[now_id].tag);        tree[now_id].tag=0;    }    if(num<=mid)        query(num,now_id<<1);    else        query(num,now_id<<1|1);}int main(){   int n;   while(~scanf("%d",&n)&&n)   {       build(1,n,1);       for(int i=1;i<=n;i++)       {           int a,b;           scanf("%d %d",&a,&b);           update(a,b,1,1);//我们上面传入的是1       }       for(int i=1;i<n;i++)       {           query(i,1);           printf(" ");//查找编号,赋值,开始编号       }       query(n,1);       printf("\n");   }   return 0;}

二、树状数组

向下查询,向上统计

#include <iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define INF 0x3f3f3f3f#define lson(x) (x<<1)#define rson(x) (x<<1|1)#define mid_line(x,y) ((x+y)>>1)#define lowbit(x) (x&(-x))using namespace std;typedef long long ll;const int maxn=1e5+10;/*第一次用树状数组来做题,明白了一点,还是一种“复制"的思想,或者说是”等效“的思想比如说,我们2-4区间,那我们就只需要在temp[4]+1,这样的话,我们就可以当成是1-4都加上了1,但是,题目只需要2-4,,所以我们对1进行temp[1]-1,这样的话,我们的区间就控制好了查询的时候,我们就可以往上走,因为上面那个”头“,储存着我们的值,我们得把它拷贝一份,加到我们的总和上对于那些标记-1,我们只是抵消了加在他上面的1,所以他也得往上找到”头“,这样的话,才算把,自己该得的拿到手*/int temp[maxn];int n;void update(int num,int val){    while(num>0)    {        temp[num] += val;        num -= lowbit(num);    }}int getSum(int num){    int sum=0;    while(num<=n)    {        sum += temp[num];        num += lowbit(num);    }    return sum;}int main(){   while(~scanf("%d",&n)&&n)   {       memset(temp,0,sizeof(temp));       for(int i=0;i<n;i++)       {           int a,b;           scanf("%d %d",&a,&b);           update(b,1);//因为树状数组没有办法控制区间,只能说是给个点,然后在0,处停止,或者是n处停止           update(a-1,-1);//这就是为了抵消我们上面的加1,因为我们只需要,a-b+1,不是1-b,+1,所以1-a,-1;       }       for(int i=1;i<n;i++)       {            printf("%d ",getSum(i));       }       printf("%d\n",getSum(n));   }   return 0;} 


向上查询,向下统计

#include <iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define INF 0x3f3f3f3f#define lson(x) (x<<1)#define rson(x) (x<<1|1)#define mid_line(x,y) ((x+y)>>1)#define lowbit(x) (x&(-x))using namespace std;typedef long long ll;const int maxn=1e5+10;/*第一次用树状数组来做题,明白了一点,还是一种“复制"的思想,或者说是”等效“的思想。还有就是”死飞“,没有刹车,不是到最后,就是到最前面,对就是这样,我们只能采取补救来消除一些错误。这种向上通知,向下统计。比如说一个区间5-10,n最大是20。开头这个人就比较猖狂,直接告诉后面我们都有了,也就是5-20都+1结果,一看不对,11开始往后传,其实我们没有,所以又都会减1,这样的话,该加的加了,该减的减了。那么我们统计的时候怎么统计呢?我们就应该向下统计了,我前面有几个开头并且到我没有结束的点,不就是我被mo的次数么*/int temp[maxn];int n;void update(int num,int val){    while(num<=n)    {        temp[num] += val;        num += lowbit(num);    }}int getSum(int num){    int sum=0;    while(num>0)    {        sum += temp[num];        num -= lowbit(num);    }    return sum;}int main(){   while(~scanf("%d",&n)&&n)   {       memset(temp,0,sizeof(temp));       for(int i=0;i<n;i++)       {           int a,b;           scanf("%d %d",&a,&b);           update(a,1);//因为树状数组没有办法控制区间,只能说是给个点,然后在0,处停止,或者是n处停止           update(b+1,-1);//这就是为了抵消我们上面的加1,因为我们只需要,a-b+1,不是1-b,+1,所以1-a,-1;       }       for(int i=1;i<n;i++)       {            printf("%d ",getSum(i));       }       printf("%d\n",getSum(n));   }   return 0;}

三、相同思想,直接累加前面的和

#include <iostream>#include<stdio.h>#include<string.h>#include<algorithm>#define INF 0x3f3f3f3f#define lson(x) (x<<1)#define rson(x) (x<<1|1)#define mid_line(x,y) ((x+y)>>1)using namespace std;typedef long long ll;const int maxn=1e5+10;int temp[maxn];int main(){    int n;   while(~scanf("%d",&n)&&n)   {       memset(temp,0,sizeof(temp));       for(int i=0;i<n;i++)       {           int a,b;           scanf("%d %d",&a,&b);           temp[a] += 1;           temp[b+1] -= 1;       }       int s=0;       for(int i=1;i<n;i++)       {            s += temp[i];            printf("%d ",s);       }       printf("%d\n",s+temp[n]);   }   return 0;}