有关树状数组例题

1——————————A - Stars

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

3
1 1
2 2
3 3
3
1 1
1 2
1 3
0

Sample Output

1 1 1
3 2 1

 

题意:

思路:

用树状数组:用更新的另一种方法——向下更新,向上求和

WHY?

树状数组在改变一个数据时整个数组的值都会改变 (牢记)

所以先向下更新来抵消对 b 以后的气球的影响

在这道题目中  需要求某一个气球涂过的次数 假如结果(a,b)  那么需要进行add(a,1);然后进行add(b+1,-1)来抵消 对b 以后的气球的影响 

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXX=5e+5;
 
int n;
int c[MAXX];
 
int lowbit(int x)
{
    return x&(-x);
}
 
void update(int i,int v)
{
    int ans=0;
    while(i)
    {
        c[i]+=v;
        i-=lowbit(i);
    }
}
 
int Getsum(int i)
{
    int ans=0;
    while(i<=n)
    {
        ans+=c[i];
        i+=lowbit(i);
    }
    return ans;
}
int main()
{
    int a,b;
    while(scanf("%d",&n)&&n!=0)
    {
        memset(c,0,sizeof(c));
        for(int i=1; i<=n; i++)
        {
            scanf("%d %d",&a,&b);
            update(b,1);
            update(a-1,-1);
        }
        for(int i=1; i<=n; i++)
        {
            if(i<n)
                printf("%d ",Getsum(i));
            else
                printf("%d\n",Getsum(i));
        }
    }
}
 
 
 
 

2—————————— B - Cows

Farmer John's cows have discovered that the clover growing along the ridge of the hill (which we can think of as a one-dimensional number line) in his field is particularly good. 

Farmer John has N cows (we number the cows from 1 to N). Each of Farmer John's N cows has a range of clover that she particularly likes (these ranges might overlap). The ranges are defined by a closed interval [S,E]. 

But some cows are strong and some are weak. Given two cows: cow i and cow j, their favourite clover range is [Si, Ei] and [Sj, Ej]. If Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj, we say that cow i is stronger than cow j. 

For each cow, how many cows are stronger than her? Farmer John needs your help!

Input

The input contains multiple test cases. 
For each test case, the first line is an integer N (1 <= N <= 10 5), which is the number of cows. Then come N lines, the i-th of which contains two integers: S and E(0 <= S < E <= 10 5) specifying the start end location respectively of a range preferred by some cow. Locations are given as distance from the start of the ridge. 

The end of the input contains a single 0.

Output

For each test case, output one line containing n space-separated integers, the i-th of which specifying the number of cows that are stronger than cow i. 

Sample Input

3
1 2
0 3
3 4
0

Sample Output

1 0 0

Hint

Huge input and output,scanf and printf is recommended.

题意:

对于每头牛,吃草的区间在 [ s , e ] 之间,对于第 i 头牛而言,如果满足   Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj

那么就说 第 i 头牛 比第 j 头牛要强壮,问比 第 i 头牛强壮的有几只

思路:

如果满足 Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj 该条件的话,间接的说明 第 i 头牛的吃草区间 [ si,ei] 包含 第 j 头牛的吃草区间

所以转化成求   给定N个子区间,问包含第 i 个 区间的区间有多少?(包含第 i 个区间的有多少个就代表了有几头牛比 i 强壮)

先对 右区间从大到小排序,如果右区间相等的话,就按左区间从小到大排序

然后 用 Getsum() 求小于等于左区间的个数,就是比他强壮的个数

CODE:

// 注释的地方都是 wa 了几发才发现的,需要注意


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXX 100000+10
int ans[MAXX];
int c[MAXX];
int maxx=-1;
int n;

struct NODE
{
    int s;
    int e;
    int num;
}node[MAXX];

bool cmp(NODE a,NODE b)
{
    if(a.e==b.e)
        return a.s<b.s;
    return a.e>b.e;
}

int lowbit(int x)
{
    return x&(-x);
}

int Getsum(int x)
{
    int sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}

void update(int x)
{
    while(x<=maxx+1) // 得到的最大值是未加 1 得到的,由于更新的时候传值加了1 ,所以maxx +1
    {
        c[x]++;
        x+=lowbit(x);
    }
}

int main()
{
    while(scanf("%d",&n)&&n!=0)
    {
        memset(ans,0,sizeof(ans));
        memset(c,0,sizeof(c));
        maxx=-1;
        for(int i=1;i<=n;i++){
            scanf("%d %d",&node[i].s,&node[i].e);
            node[i].num=i;
            maxx=max(maxx,node[i].s); // 求这几组样例中左区间的最大值,更新时的结束条件
        }
        sort(node+1,node+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            if(node[i].s==node[i-1].s&&node[i].e==node[i-1].e)
                ans[node[i].num]=ans[node[i-1].num];
            else
            {
                ans[node[i].num]=Getsum(node[i].s+1); // 由于该题能够取到 0,建树不能从0开始,所以加 1 再建树
            }
            update(node[i].s+1); // 相应的更新的时候也要加 1 
        }
        for(int i=1;i<=n;i++)
        {
            if(i<n)
                printf("%d ",ans[i]);
            else
                printf("%d\n",ans[i]);
        }
    }

}

 也可以这样写:

void update(int x)
{
    while(x<=maxx) // 此时的 maxx 是加完 1之后得到的
    {
        c[x]++;
        x+=lowbit(x);
    }
}

for(int i=1;i<=n;i++){
     scanf("%d %d",&node[i].s,&node[i].e);
     node[i].num=i;
     node[i].s++; // 先加 1 ,再去更新求值
     node[i].e++;
     maxx=max(maxx,node[i].s);
}
sort(node+1,node+n+1,cmp);
for(int i=1;i<=n;i++) 
{
     if(node[i].s==node[i-1].s&&node[i].e==node[i-1].e)
         ans[node[i].num]=ans[node[i-1].num];
     else
     {
         ans[node[i].num]=Getsum(node[i].s);
     }
         update(node[i].s); 
}

或是:

// 先更新,再求和的时候 减 1 ,因为先更新的话,已经加过 1 了


for(int i=1; i<=n; i++)
{
    update(node[i].s);
    if(node[i].s==node[i-1].s&&node[i].e==node[i-1].e)
        ans[node[i].num]=ans[node[i-1].num];
    else
    {
        ans[node[i].num]=Getsum(node[i].s)-1;
    }
}

 

3——————————C - Japan (树状数组求逆序对)

Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coast and M cities on the West coast (M <= 1000, N <= 1000). K superhighways will be build. Cities on each coast are numbered 1, 2, ... from North to South. Each superhighway is straight line and connects city on the East coast with city of the West coast. The funding for the construction is guaranteed by ACM. A major portion of the sum is determined by the number of crossings between superhighways. At most two superhighways cross at one location. Write a program that calculates the number of the crossings between superhighways.

Input

The input file starts with T - the number of test cases. Each test case starts with three numbers – N, M, K. Each of the next K lines contains two numbers – the numbers of cities connected by the superhighway. The first one is the number of the city on the East coast and second one is the number of the city of the West coast.

Output

For each test case write one line on the standard output: 
Test case (case number): (number of crossings)

Sample Input

1
3 4 4
1 4
2 3
3 2
3 1

Sample Output

Test case 1: 5

题意:东边有N个城市,南边有M个城市,在这些东南城市的中间有K条道路,每一条道路都连着一个东部城市和一个西部的城市,问这些连线有多少个交叉点。

思路:

当左边的顺序固定时(从小到大),如果想存在交叉点,那么当前的右边点必须要比之前的点小。 
比如; 
第一个道路的连接的城市为1和4,下一个道路连接东边的城市编号为2,如果想有交叉点,那么另一个编号必须要小于4。 
这样就转换成了 对南边的城市求逆序对个数的问题

前提是左边的数从小到大排序,如果相等则对右边的数从小到大排序 ( 画一下图可以得出这一中情况是没有交点的,如果按右边从大到小排序的话,多得出来一个逆序对,所以要对右边从小到大排序)

CODE:

//  当没有 给 k 的范围是,就设置成 1000×1000
//  wa 原因: 改成 long long 就A 了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXX 1000000+10
typedef long long LL;
LL c[MAXX];
int n,m;

struct NODE
{
    int a;
    int b;
}node[MAXX];

bool cmp(NODE p,NODE q)
{
    if(p.a==q.a)
        return p.b<q.b;
    return p.a<q.a;
}

int lowbit(int x)
{
    return x&(-x);
}

LL Getsum(int x)
{
    LL sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}

void update(int x)
{
    while(x<=m)
    {
        c[x]++;
        x+=lowbit(x);
    }
}

int main()
{
    int t,k;
    LL ans=0;
    int flag=1;
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        memset(c,0,sizeof(c));
        scanf("%d %d %d",&n,&m,&k);
        for(int i=1;i<=k;i++)
            scanf("%d %d",&node[i].a,&node[i].b);
        sort(node+1,node+k+1,cmp);
        for(int i=1;i<=k;i++)
        {
            update(node[i].b);
            ans+=i-Getsum(node[i].b);
        }
        printf("Test case %d: %lld\n",flag++,ans);
    }

}

 

4——————————D- Stars (树状数组)(POJ 2352)

Astronomers often examine star maps where stars are represented by points on a plane and each star has Cartesian coordinates. Let the level of a star be an amount of the stars that are not higher and not to the right of the given star. Astronomers want to know the distribution of the levels of the stars. 


For example, look at the map shown on the figure above. Level of the star number 5 is equal to 3 (it's formed by three stars with a numbers 1, 2 and 4). And the levels of the stars numbered by 2 and 4 are 1. At this map there are only one star of the level 0, two stars of the level 1, one star of the level 2, and one star of the level 3. 

You are to write a program that will count the amounts of the stars of each level on a given map.

Input

The first line of the input file contains a number of stars N (1<=N<=15000). The following N lines describe coordinates of stars (two integers X and Y per line separated by a space, 0<=X,Y<=32000). There can be only one star at one point of the plane. Stars are listed in ascending order of Y coordinate. Stars with equal Y coordinates are listed in ascending order of X coordinate. 

Output

The output should contain N lines, one number per line. The first line contains amount of stars of the level 0, the second does amount of stars of the level 1 and so on, the last line contains amount of stars of the level N-1.

Sample Input

5
1 1
5 1
7 1
3 3
5 5

Sample Output

1
2
1
1
0

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed.

 

题意:

有N个星星,给出N个星星的坐标,但是坐标给出的顺序已经有一定的规律

按照y坐标从小到大给出,当y坐标相同的时候按照x坐标的大小顺序给出;

之后输出处在各个等级(0-n-1) 的星星的个数

等级就是不能比当前星星高 并且 不能在它的右边,所以只能在他的左下角找小于等于他的个数

用 Getsum() 函数能实现该功能

等同于去找比当前星星 x 坐标小的有几个,就处在第几等级,此时不用考虑 y 的大小,因为输入的时候已经输入了 y 是逐渐增加的

并且建树的时候下必须从 1 开始,所以对于该题要先 对当前点 的 x +1 ,再去求和更新

CODE:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int ans[32002];
int c[32002];
int n;
 
int lowbit(int i)
{
    return i&(-i);
}
 
int Getsum(int i)
{
    int sum=0;
    while(i)
    {
        sum+=c[i];
        i-=lowbit(i);
    }
    return sum;
}
 
void update(int i)
{
    while(i<32002) // !!!!注意这里,不能是等于
    {
        c[i]++; // 遇见一个比它大的坐标加 1 
        i+=lowbit(i);
    }
}
int main()
{
    int x,y;
    memset(ans,0,sizeof(ans));
    memset(c,0,sizeof(c));
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d %d",&x,&y);
        x++;  // 注意这里,建树的时候要从 1 开始
        ans[Getsum(x)]++; // 该等级 加 1
        update(x);
    }
    for(int i=0; i<n; i++)
        printf("%d\n",ans[i]);
}
 
 
 
 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值