hdu4311

/*
分析:
    树状数组+二分(可以不用二分)。
    果真是流年不利么。。。一开始就考虑会爆int的,就想到用64位了,
结果代码敲着敲着、一部分还是用了int,敲傻了么,1wa,囧~。。。
    简单说下思路吧:
        假设现在有n个点,把这些点都画到坐标纸上后第i个点在最左边、
    同时也是最下面,
    那么,假设大家都到i这里来:
                                第一个人到i这里,要走x1-xi+y1-yi;
                                第二个人到i这里,要走x2-xi+y2-yi;
                                。。。。。。
                                第n个人道i这里,要走 xn-xi+yn-yi;
    (看出来思路没?)简单整合一下,不就是:
                ans=(x1+x2+x3+...+xn)-n*xi+(y1+y2+...+yn)-n*yi;
    (括号里面的sum部分可以用树状数组、线段树呀来维护)
        但是,有些x<xi、有些x>xi,怎么办呢?那就将所有的x都排个序,
    再将所有的y排个序,同时让每个x记住自己对应的y值在排序后到了哪儿,
    在排序之后,再列出这个树状数组(线段树)。
        然后对x从小到大遍历,遍历的当前的点就作为当前的中心,让大
    家都来这个点,这个时候,刚刚的“(x1+x2+...+xn)”就可以分为两个部
    分了,一部分在当前点左边,一部分在当前点右边,这个借助树状数组
    在log(n)的时间内求出x方向上的ans_x;再找到当前点对应的y值,用同
    样的方法求出y方向上的ans_y,然后就可以得到ans=ans_x+ans_y了。


    PS:
        打字打上瘾了。。。
        头有点儿晕,就懒一下,没有维护排序后x对应的y在哪里,用的方法是
    每次都二分查找一下-、-I,不过只是大概让时间*2了而已,仍在可以接受范
    围以内。。。


                                                               2012-12-12
*/









#include"stdio.h"
#include"string.h"
#include"stdlib.h"
#define N 100011

int n;
struct A
{
    int x,y;
}E[N];
int x[N],y[N];

int lowbit[N];
__int64 C[2][N];								//0x、1y

int cmp(const void *a,const void *b)
{
    return *(int *)a-*(int *)b;
}
void get_lowbit()
{
    int i;
    for(i=1;i<=100000;i++)    lowbit[i]=i&(-i);
}

__int64 sum(int K,int k)
{
    __int64 p=0;
    while(k>0 && k<=n)
    {
        p+=C[K][k];
        k-=lowbit[k];
    }
    return p;
}
void update(int K,int k,int dir)
{
    while(k>0 && k<=n)
    {
        C[K][k]+=dir;
        k+=lowbit[k];
    }
}
void init()
{
    int i;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&E[i].x,&E[i].y);
        x[i]=E[i].x;
        y[i]=E[i].y;
    }
    x[0]=y[0]=-1111111111;
    qsort(x,n+1,sizeof(int),cmp);
    qsort(y,n+1,sizeof(int),cmp);
    memset(C,0,sizeof(C));
    for(i=1;i<=n;i++)
    {
        update(0,i,x[i]);
        update(1,i,y[i]);
    }
}
__int64 solve()
{
    int i;
    int low,mid,up;
    __int64 a,b;
    __int64 sum_x,sum_y;
    __int64 temp,ans;
    ans=100011;
    ans*=1000000000;
    sum_x=sum(0,n);
    sum_y=sum(1,n);
    for(i=1;i<=n;i++)
    {
        low=1;up=n;mid=(low+up)>>1;
        while(low<=up)
        {
            if(x[mid]<E[i].x)    low=mid+1;
            else                up=mid-1;
            mid=(low+up)>>1;
        }
        a=sum(0,low);
        temp=(__int64)low*E[i].x-a+sum_x-a-(n-(__int64)low)*E[i].x;
        low=1;up=n;mid=(low+up)>>1;
        while(low<=up)
        {
            if(y[mid]<E[i].y)    low=mid+1;
            else                up=mid-1;
            mid=(low+up)>>1;
        }
        b=sum(1,low);
        temp+=(__int64)low*E[i].y-b+sum_y-b-(n-(__int64)low)*E[i].y;
        if(temp<ans)    ans=temp;
    }
    return ans;
}

int main()
{
    int T;
    get_lowbit();
    scanf("%d",&T);
    while(T--)
    {
        init();
        printf("%I64d\n",solve());
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值