pku3685 二分

来源:互联网 发布:python就业怎么样 编辑:程序博客网 时间:2024/06/06 09:40

http://poj.org/problem?id=3685

题意:一个50000*50000的矩阵,每个元素值为: i2 + 100000 × i + j2 - 100000 × j + i × j,
求这个矩阵中的第k小值。。。




分析:做法用了类似前面一篇二分的方法,先二分枚举结果val,很明显每一列是递增的,那么扫描每一列再二分求比val小的值的个数。。。。但是两个二分实在太慢了。2000+MS。。。仰慕0ms的程序。。。

发现一元二次方程的特征都忘了。。。第一次居然冒失的去枚举行去了,单调性都没分析。。。。

用解方程的方法要用double,老是结果不对,不知道为什么。。。。
------------搞了半天,原来还不是double的问题,还是二分出了点小错。。。汗啊啊啊啊啊啊。。。


代码:
300+ms

#include <stdio.h>#include <algorithm>#include <cmath>#include <iostream>using namespace std;const __int64 inf=10000000000;__int64 n, k;//通过解方程,求值<num的个数。。。。__int64 bs2(__int64 i, __int64 num){__int64 xx1, xx2;double x1, x2, delt, b, c;b = i-100000;c = i*i+100000*i-num;delt = b*b-4*c;if(delt<=0)return 0;delt = sqrt(delt);x1 = (-b+delt)/(2);x2 = (-b-delt)/(2);if(x1>x2)swap(x1, x2);xx1 = x1;xx1++;xx2 = x2;if(xx2==x2)xx2--;if(xx1>n)return 0;if(xx2<1)return 0;if(xx1<1)xx1 = 1;if(xx2>n)xx2 = n;return xx2-xx1+1;}__int64 bs() {__int64 l, r, mid, cnt, i;l=-inf, r=inf;while(l<=r){mid = (l+r)/2;cnt = 0;for(i=1; i<=n; i++)cnt += bs2(i, mid);if(cnt>=k)r = mid-1;elsel = mid+1;}return l-1;}int main(){int cas;scanf("%d", &cas);while(cas--){scanf("%I64d%I64d", &n, &k);printf("%I64d\n", bs());}return 0;}



代码:
2000+MS

#include <stdio.h>#include <algorithm>#include <cmath>#include <iostream>using namespace std;const __int64 inf=10000000000;__int64 n, k;__int64 bs1(__int64 i, __int64 num) //枚举列,每一列一定单调增的。。。{__int64 l, r, mid, tmp;l=1, r=n;while(l<=r){mid = (l+r)/2;//tmp = i*i+100000*i+mid*mid-100000*mid+i*mid;tmp = mid*mid+100000*mid+i*i-100000*i+i*mid;if(tmp<num)l = mid+1;elser = mid-1;}return r;}__int64 bs() {__int64 l, r, mid, cnt, i;l=-inf, r=inf;while(l<=r){mid = (l+r)/2;cnt = 0;for(i=1; i<=n; i++)cnt += bs1(i, mid);if(cnt>=k)r = mid-1;elsel = mid+1;}return l-1;}int main(){int cas;scanf("%d", &cas);while(cas--){scanf("%I64d%I64d", &n, &k);printf("%I64d\n", bs());}return 0;}