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; }