Chinaunix首页 | 论坛 | 博客
  • 博客访问: 342937
  • 博文数量: 122
  • 博客积分: 5000
  • 博客等级: 大校
  • 技术积分: 1191
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-24 11:12
文章分类

全部博文(122)

文章存档

2010年(122)

我的朋友

分类: C/C++

2010-04-01 15:52:47

一、问题描述

题目:

题目大意:如图所示,在平面上有N个点,每个点都有一个坐标,然后在平面上划一条水平线,划一条垂直线,这两条线把平面分成四个区域,计算1区和3区点的和与2区和4区点的和的差的绝对值,即|(a1+a3)-(a2+a4)|ai表示i区的点数目。点不会落在两条线上。要划M次线,计算每一次划线的结果。(N,M ≤ 50000, 0 ≤x,y ≤ 500000)

 

二、解题思路

由图可知,对于每一次划线,只要求得a2a12a23a1234(分别表示1区的点数量、1区和2区的点数量和,2区和3区的点数量和,所有的点数量)就能求得|(a1+a3)-(a2+a4)|。其中a1=a12-a2; a3=a23-a2;a4=a1234-a12-a3整理后得到|(a1+a3)-(a2+a4)|=|2*a12-4*a2+2*a23|

定义点的结构

struct Pos

{

       int x;

       int y;

       int index;//该点在输入时的下标(顺序)

};

输入保存在Pos p[]中。

使用一维树状数组C。先将输入各点的坐标和要划的线坐标按y从到大到小进行排序,如果y相等则按x从小到大排序。然后遍历一遍输入数据,如果是index<=N,则修改树状数组C[p[i].x];如果index>N,则计算a2=Sum(p[i].x)a12=Sum(xMax)( xMax为输入坐标中x坐标的最大值)。遍历完输入数据后,可以通过树状数组计算a1234=Sum(xMax); a23=Sum(p[ID[i]].x);ID[i]为相应的划线的下标。最后可通过上面公式计算出结果。

三、代码

#include<algorithm>
using namespace std;
int C[500001];    //树状数组
struct Pos
{
    int x;
    int y;
    int index;
};
Pos p[100002];    //保存输入的硬币坐标和划线的坐标
int a2[50002];    //a2[i](i>=1)表示按第i种划线时,2区的硬币数量
int a12[50002];    //a12[i](i>=1)表示按第i种划线时,1区和2区的硬币数量和
int ID[50002];    //ID[i]表示p[]排序后第i组划线在数组中的下标。
int n;            //树状数组的元素个数
int cmp(const void *a,const void *b)
{
    Pos * c=(Pos * )a;
    Pos * d=(Pos * )b;
    if(c->y == d->y)
        return c->x - d->x;
    else
        return d->y - c->y;
}
int Lowbit(int x)
{
    return x&(x^(x-1));
}
void Modify(int i,int x)
{
    while(i<=n)
    {
        C[i]+=x;
        i+=Lowbit(i);
    }
}
int Sum(int i)
{
    int sum=0;
    while(i>0)
    {
        sum+=C[i];
        i-=Lowbit(i);
    }
    return sum;
}
int main()
{
    int T;
    int N,M;
    int t,i;
    int xMax;//最大的x坐标
    scanf("%d",&T);
    for(t=0;t<T;++t)
    {
        memset(C,0,sizeof(C));
        scanf("%d%d",&N,&M);
        xMax=0;
        int total=N+M;
        for(i=0;i<total;++i)
        {
            scanf("%d%d",&p[i].x,&p[i].y);
            p[i].x+=1;
            p[i].y+=1;
            p[i].index=i+1;
            if(xMax<p[i].x)
                xMax=p[i].x;
        }
        n=xMax;
        qsort(p,total,sizeof(p[0]),cmp);
        for(i=0;i<total;++i)
        {
            if(p[i].index>N)
            {
                //计算2区硬币数量
                a2[ p[i].index-N ]=Sum(p[i].x);
                //计算1区和2区硬币数量之和
                a12[ p[i].index-N ]=Sum(xMax);
                //将该划线的下标保存起来,用于后面计算a23时使用
                ID[ p[i].index-N ]=i;
            }
            else
            {
                Modify(p[i].x,1);//修改树状数组
            }
        }
        int a1234=Sum(xMax);//计算总和
        for(i=1;i<=M;++i)
        {
            int a23=Sum(p[ID[i]].x);
            int res=2*a12[i]-4*a2[i]+2*a23-a1234;
            if(res<0)
                res*=(-1);
            printf("%d\n",res);
        }
        printf("\n");
    }
    return 0;
}


阅读(578) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~