树状数组 例题

 具体模板详见  https://blog.csdn.net/zcy19990813/article/details/81297476

Color the ball

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

 AC代码:

#include<iostream>
#include <algorithm>
#include <cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
const int M=200000+10;
const int MAX=0x3f3f3f3f;
typedef long long ll;
ll a[101010],c[101010],t[101010],n;
ll lowbit(ll x)
{
    return x&(-x);
}
ll Getsum(ll i)
{
    ll ans=0;
    while(i>0)
    {
        ans+=c[i];
        i-=lowbit(i);
    }
    return ans;
}
void update(ll i,ll v)
{
    while(i<=n)
    {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int main()
{
    ll m,i,j,aa,bb;
    while(~scanf("%lld",&n)&&n)
    {
        memset(a,0,sizeof(a));
        memset(c,0,sizeof(c));
        for(i=1;i<=n;i++)
        {
            scanf("%lld%lld",&aa,&bb);
            update(aa,1);
            update(bb+1,-1);
        }
        for(i=1;i<n;i++)
          printf("%lld ",Getsum(i));
        printf("%lld\n",Getsum(n));
    }
    return 0;
}

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颗星星,排列在笛卡尔坐标中,输入时按y坐标递增,y坐标相等时,按x坐标递增。求最后每颗星星左下方的星星数

给出n个stars的坐标,对于当前的star,如果有k个 stars的横纵坐标均不大于此star的横纵坐标,那么这个star的等级就为k
输出的n行代表等级从0-n-1的star的个数。

这道题的难点在于如何用树状数组来存各个坐标星星的等级。

题意已说明输入的的时候是按Y坐标从小到大排序的,然后可以用一个数组来根据横坐标的大小来记录这个点的星星的的等级。

首先每次读入一个星星的位置后,在它属于的等级的个数+1,然后要更新它之后的横坐标的等级。

#include<iostream>
#include <algorithm>
#include <cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
const int M=200100;
const int MAX=0x3f3f3f3f;
typedef long long ll;
ll a[301010],c[301010],t[301010],n;
ll lowbit(ll x)
{
    return x&(-x);
}
ll Getsum(ll i)
{
    ll ans=0;
    while(i>0)
    {
        ans+=c[i];
        i-=lowbit(i);
    }
    return ans;
}
void update(ll i,ll v)
{
    while(i<=32001)
    {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int main()
{
    ll m,i,j,aa,bb;
    scanf("%lld",&n);
    memset(a,0,sizeof(a));
    memset(c,0,sizeof(c));
    memset(t,0,sizeof(t));
    for(i=0; i<n; i++)
    {
        scanf("%lld%lld",&aa,&bb);
        aa++;
        t[Getsum(aa)]++;
        update(aa,1);
    }
    for(i=0; i<n; i++)
        printf("%lld\n",t[i]);
    return 0;
}

逆序对

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。
这样就转换成了 对南边的城市求逆序对个数的问题(前提是东边城市编号从小到大排序,如果相等则对南边城市编号从小到大排序)

#include<iostream>
#include <algorithm>
#include <cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
const int M=1e6;
const int MAX=0x3f3f3f3f;
typedef long long ll;
ll a[M],c[M],t[M],n,m,k;
struct Road
{
    ll e;
    ll w;
}road[M];
bool cmp(struct Road p,struct Road q)
{
    if(p.e==q.e)
        return p.w<q.w;
    return p.e<q.e;
}
ll lowbit(ll x)
{
    return x&(-x);
}
ll Getsum(ll i)
{
    ll ans=0;
    while(i>0)
    {
        ans+=c[i];
        i-=lowbit(i);
    }
    return ans;
}
void update(ll i,ll v)
{
    while(i<=m)
    {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int main()
{
    ll i,j,aa,bb,ee=1,T;
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        memset(a,0,sizeof(a));
        memset(c,0,sizeof(c));
        memset(t,0,sizeof(t));
        for(i=1;i<=k;i++)
            scanf("%lld%lld",&road[i].e,&road[i].w);
        sort(road+1,road+k+1,cmp);
        ll ans=0;
        for(i=1;i<=k;i++)
        {
            update(road[i].w,1);
            ans+=i-Getsum(road[i].w);
        }
        printf("Test case %lld: %lld\n",ee,ans);
        ee++;
    }
    return 0;
}

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.

题意:一头牛喜欢吃的草的范围为[ Si, Ei ],如果这个区间包含在另一头牛的区间里,那么就说明另一头牛比这头牛更强壮,求对于第 i 头牛,比它强壮的牛的个数。

就是求一个区间的真子集(注意的是,两头牛的范围可能相同)

思路:
此题和Stars那道题差不多,都是先固定一侧的区间,然后对另一侧区间进行操作。但是这道题固定的是右区间,按照有区间从大到小排序,如果相同,就让左区间从小到大排序。

这样就能保证对于每头牛的右区间都小于等于在之前牛的右区间,那么,只需要找对于当前的牛的左区间,前面比它小的个数就行了。

细节处理:
范围+1,防止树状数组里出现0;
每次找到比当前左区间小的个数的时候还要减一,去除本身;
当遇到两头牛的范围相等时,令答案等于上一头牛的答案即可,无需求和。

#include<iostream>
#include <algorithm>
#include <cstdlib>
#include<cstdio>
#include<cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const ll M=1e6;
ll MAX=-0x3f3f3f3f;
ll a[M],c[M],anss[M];
struct Road
{
    ll e;
    ll w;
    ll h;
}road[M];
bool cmp(struct Road p,struct Road q)
{
    if(p.w==q.w)
        return p.e<q.e;
    return p.w>q.w;
}
ll lowbit(ll x)
{
    return x&(-x);
}
ll Getsum(ll i)
{
    ll ans=0;
    while(i>0)
    {
        ans+=c[i];
        i-=lowbit(i);
    }
    return ans;
}
void update(ll i,ll v)
{
    while(i<=MAX)
    {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int main()
{
    ll i,j,n;
    while(~scanf("%lld",&n)&n)
    {
        memset(c,0,sizeof(c));
        for(i=1;i<=n;i++)
        {
            scanf("%lld%lld",&road[i].e,&road[i].w);
            road[i].h=i;
            road[i].e++;road[i].w++;
            MAX=max(MAX,road[i].w);
        }
        sort(road+1,road+n+1,cmp);
        for( i=1; i<=n; i++)
        {
            update(road[i].e,1);
            if(i==1)
                anss[road[i].h]=Getsum(road[i].e)-1;
            else
            {
                if(road[i].e==road[i-1].e&&road[i].w==road[i-1].w)
                    anss[road[i].h]=anss[road[i-1].h];
                else
                    anss[road[i].h]=Getsum(road[i].e)-1;
            }
        }
        for(i=1;i<=n;i++)
        {
            if(i==n)
              printf("%lld\n",anss[i]);
            else
              printf("%lld ",anss[i]);
        }
    }
    return 0;
}

若不理解  请看大佬博客  (一位超厉害的师哥)  https://blog.csdn.net/akatsuki__itachi/article/category/7171497

posted @ 2018-07-31 13:24  ~~zcy  阅读(148)  评论(0编辑  收藏  举报