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

比赛地址
密码: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 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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 0x3f3f3f3f
typedef 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;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值