2013 多校第三场 hdu 4631 Sad Love Story

hdu 4631

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4631

题目大意:一个平面,先开始没有点,然后分别给你一个前一个点坐标和当前点坐标x、y的递推式,每次新加入一个点,总共加入n个点,每加入一个点(当点数>1时),有一个距离最近的点对,要求的就把这n-1个最小的距离加起来的值。

思路:用一个set保存点,并按照x升序排,然后对于要新加入的点,分别从按两边开始找,如果两个点的(x1-x2)^2已经大于minn了,那么就退出。其实就是暴力+STL+一个剪枝。按照题解的说法就是因为minn下降很快,而且点又是随机生成的,所以更新的点没几个,复杂度应该小于O(nlogn)。

很暴力有木有。。  = = 比赛的时候看了这道题,第一想法就是暴力,可是一算复杂度,感觉是O(n^2),然后就没敢敲,其实当初也没有看时间,20s,是可以去飘一下。后来一看题解,果然也是暴力,只不过我们当初想的是优先队列,他是用set,因为set用的不熟。。 现在想想,其实优先队列也不好查找。。以后多用用STL吧,特别是set这种的多用用。。

代码里还有一个地方要注意的,那就是set里查找的时候,要用s.lower_bound(),而不要直接用lower_bound(),时间差很多的!我那个程序一改后面那种,直接TLE。。 = =

代码如下:

#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;

#define MP make_pair

typedef __int64 lld;

const lld INF = 0x0fffffffffffffff ;

struct XY
{
    lld A,B,C;
} X,Y;

set < pair<lld,lld> > s;

lld cal(lld x,int flag)
{
    if(flag==0)
    {
        return (x*X.A+X.B)%X.C;
    }
    else return (x*Y.A+Y.B)%Y.C;
}

lld cal_dis(lld x1,lld y1,lld x2,lld y2)
{
    return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%I64d%I64d%I64d%I64d%I64d%I64d",&X.A , &X.B , &X.C , &Y.A , &Y.B , &Y.C);
        s.clear();
        lld pre_x = cal(0,0);
        lld pre_y = cal(0,1);
        s.insert(MP(pre_x,pre_y));
        set< pair <lld,lld> > :: iterator it,pos;
        lld minn = INF;
        lld ans=0;
        for(int i=2;i<=n;i++)
        {
            lld x = cal(pre_x,0);
            lld y = cal(pre_y,1);
            pos = s.lower_bound(MP(x,y));
            //pos = lower_bound(s.begin(),s.end(),MP(x,y)); 不要这么写,时间差很多,直接超时了
            for(it = pos ; it!=s.begin() ;)
            {
                it--;
                if((x-(*it).first)*(x-(*it).first)>=minn)
                    break;
                else
                {
                    minn = min(minn,cal_dis(x,y,(*it).first,(*it).second));
                }
            }
            for(it = pos ; it!=s.end() ;it++)
            {
                if((x-(*it).first)*(x-(*it).first)>=minn)
                    break;
                else
                {
                    minn = min(minn,cal_dis(x,y,(*it).first,(*it).second));
                }
            }
            s.insert(MP(x,y));
            //printf("minn = %I64d\n",minn);
            pre_x = x;
            pre_y = y;
            ans += minn;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值