sduacm2016级暑假集训 二分&尺取&三分

来源:互联网 发布:人工智能的英语怎么写 编辑:程序博客网 时间:2024/06/15 05:26

比赛地址
密码:acm2016

A - Can you solve this equation? (HDU2199)

题目链接

【题意】
给出实数y,求方程8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 == Y在0~100范围内的解。
精确到1e-4。
【分析】
令f(x)=8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 -Y,可以发现这个函数满足单调性,故可以二分答案求解。
【Code】

#include<cstdio>#include<cstring>#include<cmath>#include<iostream>#include<algorithm>#include<set>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const double PI = acos(-1.0);const int MAX_N = 100000 + 10;const double EPS = 1e-10;double y;double calc(double x){    return 8*x*x*x*x + 7*x*x*x + 2*x*x + 3*x + 6;}int main(){    int Case;    scanf("%d",&Case);    while (Case--){        scanf("%lf",&y);        double L = 0, R = 100, mid;        for (int i = 0; i < 100; i++){            mid = (R+L) / 2;           //printf("%.2f  %.5f\n",mid, calc(mid));            if (calc(mid) + EPS < y) L = mid;            else R = mid;        }        if ((L <= EPS )||(R + EPS>= 100)){            printf("No solution!\n");            continue;        }        printf("%.4f\n",L);    }}

B - The Meeting Place Cannot Be Changed (Codeforces 780B)

题目链接

【题意】
给n个人,第i个人在x[i]这个位置,他的速度为v[i],在第t分钟能到的范围为
[x[i] - t * v[i], x[i] + t * v[i]],求它们相聚的最短时间。
【分析】
也是很明显如果在第t分钟可以相聚,在大于t的时间也一定可以相聚。
然后二分时间,然后O(n)的判断是否可行,注意二分L,R的边界。
判断是否可行就是看是否有一个点能让所有人都到这个点就好,只需要lmax>=rmin就好。
【Code】

#include<cstdio>#include<cstring>#include<cmath>#include<iostream>#include<algorithm>#include<set>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const double PI = acos(-1.0);const int MAX_N = 100000 + 10;const double EPS = 1e-8;double x[MAX_N], v[MAX_N];int n;bool check(double t){    double x1 = 1e9, x2 = 0;    for (int i = 0;i < n;i++){        double l = x[i] - v[i]*t;        double r = x[i] + v[i]*t;        //l = max(l,1); r = min(r,1e9);        x1 = min(x1,r);        x2 = max(x2,l);    }    if (x2<=x1) return true;    return false;}int main(){    scanf("%d",&n);    for (int i = 0; i < n; i++) scanf("%lf",&x[i]);    for (int i = 0; i < n; i++) scanf("%lf",&v[i]);    double L = 0.0, R = 1e9, mid;    for (int i = 0; i < 1000; i++){        mid = (L + R) / 2;        if (check(mid)) R = mid;        else L = mid;        //printf("%d %.12f\n",check(mid),mid);    }    printf("%.12f\n",L);    return 0;}

C - Cable master (POJ1064)

题目链接

【题意】
有n个绳子,要得到k份长度相等的绳子,问绳子最长能有多长。
【分析】
显然答案具有单调性,然后二分判断就好。注意输出不能直接输出答案,那样会四舍五入可以*100取整再 *0.01
【Code】

#include <cstdio>#include <cstring>#include <cmath>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const int MAX_N = 20000;double a[MAX_N];int n,k;bool check(double mid){    int cnt = 0;    for (int i = 0;i < n;i++){        cnt += (int) (a[i]/mid);        if (cnt>=k) return true;    }    return false;}int main(){    scanf("%d%d",&n,&k);    double l = 0, r = 0 , mid;    for (int i = 0;i < n;i++){        scanf("%lf", &a[i]);        r = max(r,a[i]);    }    for (int i = 0;i < 100;i++){        mid = (r + l) /2;        if (check(mid)) l = mid;        else r = mid;        //printf("%d %.5f\n", check(mid), mid);    }    printf("%.2f\n",(int)(l*100)*0.01);    return 0;}

D - Median (POJ3579)

题目链接

【题意】
给出N个数,X1到XN,会有M个数对Xi,Xj 构成|Xi - Xj|(1<=i

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const int MAX_N = 100000 + 10;int a[MAX_N];int n,tmp;bool check(int x){    int cnt = 0;    for(int i = 0;i < n;i++)    {       int t = upper_bound(a, a + n, a[i] + x) - a;       cnt += t - i - 1;    }    if(cnt >= tmp) return true;    return false;}int main(){    while(~scanf("%d", &n)){         for (int i = 0;i < n;i++) scanf("%d", &a[i]);         sort(a, a + n);         int m = n * (n - 1) / 2;         tmp = (m + 1) / 2;         int l = 0, r = a[n - 1] - a[0];         int ans;         while(l <= r)         {             int mid = (l + r) >> 1;             if (check(mid)){                 ans = mid;                 r = mid - 1;             }else l = mid + 1;         }         printf("%d\n", ans);    }    return 0;}

E - NPY and shot(HDU5144)

题目链接

【题意】
给出初始高度和初速度,求不同角度斜抛运动可得的最长距离
【分析】
设角度为p
根据相关物理可知
vx = v * cos(p);
vy = v * sin(p);
t = (vy + sqrt(vy * vy + 2 * g * h)) / g
x = vx * t;
可以看出x随着p的变化先增后减,所以可以三分求解.

【Code】

#include<cstdio>#include<cstring>#include<cmath>#include<iostream>#include<algorithm>#include<set>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const double PI = acos(-1.0);const int MAX_N = 100000 + 10;const double g = 9.8;double h,v;double Dist(double degree){    double vx = v * cos(degree);    double vy = v * sin(degree);    double t = (vy + sqrt(vy * vy + 2 * g * h)) / g;    return t * vx ;}int main(){    int Case;    scanf("%d",&Case);    while (Case--){        scanf("%lf%lf",&h,&v);        double L = 0, R = 90;        double mid1, mid2, ans = 0;        while (fabs(R-L)>=1e-6){            mid1 = (L * 2 + R) / 3;            mid2 = (L + 2 * R) / 3;            double L1 = Dist(mid1);            double L2 = Dist(mid2);            ans = max(ans, max(L1, L2));            if (L2+1e-6>L1) L = mid1;            else R = mid2;        }        printf("%.2f\n",ans);    }}

F - Robin Hood (Codeforces 671B)

题目链接

【题意】
有n个人,每个人a[i]个物品,进行k次操作,每次都从最富有的人手里拿走一个物品给最穷的人。
问k次操作以后,物品最多的人和物品最少的人相差几个物品
【分析】
如果次数足够多的话,最后的肯定在平均值上下,小的最多被补到sum/n,大最多减少到sum/n,或者sum/n+1。
然后就二分最小值,看所有小的是否能在k次被填满 ;
二分最大值,看所有大的是否都在k次被抹平。
然后就是二分的写法,小和大的不一样,需要加等号,避免死循环。
【Code】

#include<cstdio>#include<cstring>#include<cmath>#include<iostream>#include<algorithm>#include<set>using namespace std;#define INF 0x3f3f3f3ftypedef long long LL;const double PI = acos(-1.0);const int MAX_N = 500000 + 10;int n, k;int a[MAX_N];LL calc1(int mid){    LL tot = 0;    for (int i = 0;i < n;i++)        if (a[i]<=mid)            tot += mid - a[i];    return tot;}LL calc2(int mid){    LL tot = 0;    for (int i = 0;i < n;i++)        if (a[i]>mid)            tot += a[i] - mid;    return tot;}int main(){    scanf ("%d%d", &n, &k);    LL sum = 0LL;    for (int i = 0;i < n;i++){        scanf("%d",&a[i]);        sum += a[i];    }    sort(a, a + n);    int L = sum / n, R = (sum + n - 1) / n;    int l = 0, r = L, ansL = 0, ansR = 0;    while (l <= r){        int mid = (l + r) >> 1;        if (calc1(mid) <= k) {            ansL = mid;            l = mid + 1;        }else r = mid-1;    }    l = R;r = 1e9;    while (l <= r){        int mid = (l + r) >> 1;        if (calc2(mid) <= k) {            ansR = mid;            r =mid - 1;        }else l = mid + 1;    }    printf("%d\n",ansR - ansL);    return 0;}

G - Vasya and String (Codeforces 676C)

题目链接

【题意】
给你一个长度为n的只含a,b的字符串,最多改变其中的k个字符,求可以得到的最长的连续子序列。
【分析】
可以用二分或尺取法解决。
二分:
枚举起点,二分末点,判断这段的要替换的字符的数量是否小于等于k,这个字符数量用前缀和处理。
需要对替换a和替换b二分两遍。
尺取法:
同样需要两遍尺取,L,R为区间,
如果当前子序列L,R的需要替换的字符<=k就R++,否则L++使需要替换的字符

#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <algorithm>using namespace std;char s[100100];int main(){    int n,k;    scanf("%d%d",&n,&k);    scanf("%s",s);    queue <char> Q;    int maxn=0;    int len=0;    for (int i=0; i<n; i++)    {        if (s[i]=='a')            Q.push(s[i]);        else if (len<k)        {            len++;            Q.push(s[i]);        }        else        {            maxn=max(maxn,(int)Q.size());            while (!Q.empty()&&Q.front()=='a')                Q.pop();            if (!Q.empty())            {                Q.pop();                Q.push(s[i]);            }        }    }    maxn=max(maxn,(int)Q.size());    while (!Q.empty()) Q.pop();    len=0;    for (int i=0; i<n; i++)    {        if (s[i]=='b')            Q.push(s[i]);        else if (len<k)        {            len++;            Q.push(s[i]);        }        else        {            maxn=max(maxn,(int)Q.size());            while (!Q.empty()&&Q.front()=='b')                Q.pop();            if (!Q.empty())            {                Q.pop();                Q.push(s[i]);            }        }    }    maxn=max(maxn,(int)Q.size());    printf("%d\n",maxn);    return 0;
原创粉丝点击