最近好像高频遇见三类题:
利用C++ STL会炒鸡机智+优雅的模拟...
尺取法...
二分搜索...
二分搜索是O(log(n))的,然后,要求询问内容在区间内单调。
UVALive 7292 Refract Facts
如题,求角度。
关于double的二分,如果不加break条件会死循环,然后,转移条件:R = mid or L = mid.
由公式的两个角度,对其中一个二分即可。
注意精度问题,1e-4 WA sample3. 1e-5 AC. 常规来讲,大概可以是1e-10 或者 .. 1e-99 ? 2333
#include <stdio.h> #include <iostream> #include <string.h> #include <math.h> #include <cmath> #define pie acos(-1) #define eps 1e-5 using namespace std; int main() { //freopen("in.cpp", "r", stdin); double d, h, x, n1, n2; while(~scanf("%lf%lf%lf%lf%lf", &d, &h, &x, &n1, &n2)) { if (d == 0 && h == 0 && x == 0 && n1 == 0 && n2 == 0) break; double l=0, r = pie/2; double mid; while(r-l>eps) { mid = (l+r)*1.0/2; // degree1 double deg2 = asin(n2*1.0/n1*sin(mid)); if (abs(d*tan(mid) + h*tan(deg2) - x) < 1e-5) break; if (d*tan(mid) + h*tan(deg2) - x > eps) { r = mid; } else { l = mid; } } printf("%.2f\n", 90.0 - mid*180/pie); } return 0; }
CF 689D Friends and Subsequences
题意:给两个数组,问有多少个区间满足该区间内a[]的最大值和b[]的最小值。
关于区间询问的常见算法:
线段树,莫队,RMQ,询问都是O(1)的,如果是依次询问,会有n*(n+1)/2个,n <=2*10^5,毫无疑问TLE。挣扎了一下,挂在sample 7.
所以应该想到,肯定不会是n*(n+1)/2个询问。可以发现,对任意的一个区间,固定左端点,maxa - minb 的值是单调递增的。单调递增?也就是说,对于任意
的一个左端点,与之对应的右端点一定是一个连续的区间或者不存在。那么,我们就可以通过二分查找来确定右端点的边界。
确定左边界时:寻找第一个maxa ==minb 的值,所以当前==的时候,也要左移。 if (maxa <= minb) 左移;else 右移;
同理,确定右边界时:寻找最后一个maxa == minb的值,所以==的时候,右移。if (maxa < minb) 左移;else 右移;
时间复杂度:RMQ预处理O(n*log(N)) ,二分查找:O(log(N))。
#include <stdio.h> #include <iostream> #include <string.h> #define maxn 200100 #define LL long long using namespace std; int a[maxn], b[maxn]; int dpa[maxn][20], dpb[maxn][20]; int mm[maxn]; int max_(int a, int b) { return a > b ? a : b; } int min_(int a, int b) { return a < b ? a : b; } void initRMQa(int n, int a[]) { mm[0] = -1; for (int i=1; i<=n; ++i) { mm[i] = ((i&(i-1)) == 0) ? mm[i-1]+1 :mm[i-1]; dpa[i][0] = a[i]; } for (int j=1; j<=mm[n]; ++j) { for (int i=1; i+(1<<j)-1<=n; ++i) { int ta = dpa[i][j-1]; int tb = dpa[i+(1<<(j-1))][j-1]; dpa[i][j] = max_(ta, tb); } } } void initRMQb(int n, int a[]) { mm[0] = -1; for (int i=1; i<=n; ++i) { mm[i] = ((i&(i-1)) == 0) ? mm[i-1]+1 :mm[i-1]; dpb[i][0] = b[i]; } for (int j=1; j<=mm[n]; ++j) { for (int i=1; i+(1<<j)-1<=n; ++i) { int ta = dpb[i][j-1]; int tb = dpb[i+(1<<(j-1))][j-1]; dpb[i][j] = min_(ta, tb); } } } int queryA(int x, int y) { int k = mm[y-x+1]; return max_(dpa[x][k], dpa[y-(1<<k)+1][k]); } int queryB(int x, int y) { int k = mm[y-x+1]; return min_(dpb[x][k], dpb[y-(1<<k)+1][k]); } int main(){ // freopen("in.cpp", "r", stdin); int n; while(~scanf("%d", &n)) { for (int i=1; i<=n; ++i) scanf("%d", &a[i]); for (int i=1; i<=n; ++i) scanf("%d", &b[i]); initRMQa(n, a); initRMQb(n, b); LL ans = 0; // solve for (int i=1; i<=n; ++i) { int ansl = -1, ansr = -1; int l = i, r = n; while(l<=r) { // l int mid = (l+r)/2; int maxa = queryA(i, mid); int minb = queryB(i, mid); if (maxa >= minb) { r = mid - 1; if (maxa == minb) ansl = mid; } else l = mid + 1; } l = i, r = n; int cnt = 0; while(l<=r) { while(l<=r) { //r int mid = (l+r)/2; int maxa = queryA(i, mid); int minb = queryB(i, mid); if (maxa > minb) { r = mid - 1; } else { l = mid + 1; if (maxa == minb) ansr = mid; } } } if (ansl != -1 && ansr != -1 && ansl <= ansr) { ans += (ansr - ansl + 1); } } printf("%I64d\n", ans); } return 0; }
CF 689C Mike and Chocolate Thieves1
题意:给出一个数m,问最小的n满足,所有小于等于n的数组成的k倍序列(a a*k a*k^2 a*k^3)刚好是10个。
可以发现每个元组由a唯一确定,那么,对于数n,若m=n / (k*K*k) ,则说明a 的取值可以是 [1, m]的其中一个,即可以确定这样的序列有m个。
于是...对 1和 1e18区间内的数,二分查找符合题意的n的最小值...就可以了..
也就是说..此时我们已经知道了如何判断当前数是否符合条件,并且能够在不断的二分区间的情况下,找到正确的答案。时间O(log(N))。
#include <stdio.h> #include <string.h> #include <iostream> #define LL long long using namespace std; LL check(LL n) { LL i; LL ans = 0; for (i=2; i*i*i<=n; ++i) { ans += n / (i*i*i); } return ans; } int main() { // freopen("in.cpp", "r", stdin); LL n; while (~scanf("%I64dd", &n)) { LL l = 1, r = 1e18; LL ans = 0; while(l <= r) { LL mid = (l + r) / 2; LL temp = check(mid); if (temp >= n) { r = mid - 1; if (temp == n) ans = mid; } else l = mid + 1; } if (ans == 0) printf("-1\n"); else printf("%I64d\n", ans); } return 0; }//
即发现了..树状数组很优雅...动态规划很优雅...之后...成功发现了二分也很优雅的事实....23333