2017暑期集训 Day 3

A - Can you solve this equation?

[Problem]
询问 8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 == Y 的 小数解
[Solution]
显然,该函数单调,直接二分答案即可,最近get到二分新技巧,无脑for循环100次
[Code]

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
int y;
double cal(double x)
{
    double tot = 8 * x * x * x * x + 7 * x * x * x + 2 * x * x + 3 * x + 6 - y;
    return tot ;
}
bool calc(double x)
{
    double tot = 8 * x * x * x * x + 7 * x * x * x + 2 * x * x + 3 * x + 6 - y;
    return tot >= 0;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &y);
        double l = 0, r = 100;
        double k = (cal(0) * cal(100));
        if (k > 0)
        {
            printf("No solution!\n");
            continue;
        }
        double ans = -1;
        for(int i = 1; i <= 100; i++)
        {
           // printf("%.4f %.4f\n", l, r);
            double mid = (l + r) / 2;
            if (calc(mid))
            {
                ans = mid;
                r = mid;
            } else
                l = mid;
        }
        printf("%.4f\n", ans);
    }
    return 0;
}

B - The Meeting Place Cannot Be Changed

[problem]
给定n个人在数轴上的位置,每个人可以选择往左走、往右走,也可静止不懂,询问n个人最短多少时间相遇
[Solution]
二分时间,通过O(n)的时间处理出每个人在mid时间后位置的可能区间,最后求一下n个区间的交,如果不为空,则r=mid-1,否则l=mid + 1
[Code]

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
const int N = 60000 + 500;
double a[N], v[N];
int n;
bool calc(double mid)
{
    double ll =  a[1] - mid * v[1];
    double rr =  a[1] + mid * v[1];
    for(int i = 2; i <= n; i++)
    {
        double s =  a[i] - mid * v[i];
        double t =  a[i] + mid * v[i];
        ll = max(ll, s);
        rr = min(rr, t);
        if (ll > rr)
            return false;
    }
    return true;
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lf", &a[i]);
    for(int i = 1; i <= n; i++)
        scanf("%lf", &v[i]);
    double l = 0, r = 1000000000;
    double ans = -1;
    for(int i = 1; i <= 100; i++)
    {
     //   printf("%.4f   %.4f\n", l, r);
        double mid = (l + r) / 2;
        if (calc(mid))
        {
            ans = mid;
            r = mid;
        } else
            l = mid;
    }
    printf("%.12f", ans);
    return 0;
}

C - Cable master

[Solution]
经典的二分题目
[Ps]
1.注意此题槽点比较多,首先题目描述的centimeter与kilometer这种需要注意,l的起始位置为
0.01
2.mgj,最后输出的时候不能四舍五入,需要舍去后面的一些位

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
using namespace std;
map<int, string> fx;
const int N = 60000 + 500;
double a[N], v[N];
int n;
bool calc(double mid)
{
    double ll =  a[1] - mid * v[1];
    double rr =  a[1] + mid * v[1];
    for(int i = 2; i <= n; i++)
    {
        double s =  a[i] - mid * v[i];
        double t =  a[i] + mid * v[i];
        ll = max(ll, s);
        rr = min(rr, t);
        if (ll > rr)
            return false;
    }
    return true;
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%lf", &a[i]);
    for(int i = 1; i <= n; i++)
        scanf("%lf", &v[i]);
    double l = 0, r = 1000000000;
    double ans = -1;
    for(int i = 1; i <= 100; i++)
    {
     //   printf("%.4f   %.4f\n", l, r);
        double mid = (l + r) / 2;
        if (calc(mid))
        {
            ans = mid;
            r = mid;
        } else
            l = mid;
    }
    printf("%.12f", ans);
    return 0;
}

D - Median

[Problem]
给定n个整数,这n个整数可以形成C(n,2)个|ai - aj|,询问中位数
[Solution]
我们先将序列a排序
显然,我们固定一个元素i,则a[i + 1] - a[i], a[i + 2] - a[i], …a[n] - a[i] 升序,这样会形成n-1个有序序列,询问这所有数中的中位数,我们二分中位数,每次确保比mid小的数的个数,最后列加一下,与1/2总数比较一下即可,注意统计比mid小的时候仍需要二分,可以用STL

lower_bound(a + 1, a + n, x) - a;  //返回[1, n - 1]中第一个大于等于x的下标  
upper_bound(a + 1, a + n, x) - a; // 返回[1, n - 1]中第一个大于x的下标

[Code]

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
map<int, string> fx;
const int N = 100000 + 500;
int a[N], c[N];
int n;
long long m;
bool calc(int mid)
{
    long long  ans = 0;
    for(int i = 1; i < n; i++)
    {
        int x = upper_bound(a + i + 1, a + n + 1, a[i] + mid) - a;
        ans += x - i - 1;
        if (ans >= m)
            return true;
    }
    return false;
}
int main()
{
    while(~scanf("%d", &n))
    {
        int kk = (n - 1) * n / 2;
        m = 1LL * (kk + 1) / 2;
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        sort(a + 1, a + n + 1);
        int l = 0, r = 1000000000;
        int ans = -1;
        while(l <= r)
        {
           // printf("%d %d\n", l, r);
            int mid = (l + r) >> 1;
            if (calc(mid))
            {
                r = mid -1;
                ans = mid;
            }
            else
                l = mid + 1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

E - NPY and shot

[Solution]
三分。
目标函数为凸函数,用三分求解即可,最近get三分技巧,
[templet]

for(int i = 1;i <= 100; i++)
{
    double mid = l + (r - l) / 3;
    double mmid = r - (r - l) / 3;
    if (calc(mid) > calc(mmid))  //在凸函数的情况下,mmid和r均在极值的右侧
         r = mmid;  //  
    else
         l = mid; 
} 

[Code]

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
map<int, string> fx;
const int N = 100000 + 500;
int a[N], c[N];
int n;
long long m;
double v0, H;
const double pi = acos(-1.0);
double calc(double p)
{
    double t1, t2, h1, t, v1, v2;
    v1 = v0 * sin(p);
    v2 = v0 * cos(p);
    t1 = v1 / 9.8;
    h1 = v1 * t1 / 2 + H;
    t2 = sqrt(2 * h1 / 9.8);
    t = t1 + t2;
    return t * v2;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%lf%lf", &H, &v0);
        double tot = calc(pi / 4);
        double l = 0, r = pi / 2;
        for(int i = 1; i <= 100; i++)
        {
            double mid = l + (r - l) / 3;
            double mmid = r - (r - l) / 3;
            if (calc(mid) > calc(mmid))
                r = mmid;
            else
                l = mid;
        }
        double ans = calc(l);
        printf("%.2f\n", ans);
    }
    return 0;
}

F - Robin Hood

[Problem]

给定n个数,每次操作把最大的数减一,最小的数加一,询问k次操作后数组最大值与最小值差值。
[Solution]
对于每次操作,一个数加一,一个数减一,我们把所有的数整体看待的话,相当于从最大的数开始,减掉k个一,使得最大值尽可能地小,同理,另一边对应的操作是使最小值尽可能地大,这样用两个二分就可以解决了,注意差值为0和为负值的情况,如果判断可以满足差值为0,则差值为0,否则差值为1
[Code]

#include<cstdio>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 500000 + 500;
int a[N];
int n, m;
bool calc1(int mid)
{
    int tot = 0;
    for(int i = 1; i <= n; i++)
        if (a[i] < mid)
    {
        tot += mid - a[i];
        if (tot > m)
            return false;
    }
    return true;
}
bool calc2(int mid)
{
    int tot = 0;
    for(int i = 1; i <= n; i++)
        if (a[i] > mid)
    {
        tot += a[i] - mid;
        if (tot > m)
            return false;
    }
    return true;
}
int main()
{
    scanf("%d%d", &n, &m);
    long long sum = 0;
    for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            sum += a[i];
        }
    int ans1 = -1;
    int l = 1, r = 1000000000;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if (calc1(mid))
        {
            ans1 = mid;
            l = mid + 1;
        }else
        r = mid -1 ;
    }
    int ans2 = -1;
    l = 1;
    r = 1000000000;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if (calc2(mid))
        {
            ans2 = mid;
            r = mid - 1;
        }else
        l = mid + 1;
    }
    int ans = ans2 - ans1;
    if (ans <= 0)
        if (sum % n == 0)
          ans = 0;
        else
          ans = 1;
    printf("%d", ans);
    return 0;
}

G - Vasya and String

[Solution]
尺取法,自己的姿势貌似不太清晰,导致wa了好多次
[Code]

#include<cstdio>
#include<iostream>
using namespace std;
const int N = 100000 + 500;
string s;
int n, m;
int main()
{
 //   freopen("b.in", "r", stdin);
    int n, m;
    scanf("%d%d", &n, &m);
    cin>>s;
    int l = 0, r = 0;
    int ans = 0;
    int sum = 0;
    if (s[0] == 'a')
        sum++;
    while(true)
    {
        while(sum <= m && r < n)
        {
            ans = max(ans, r - l + 1);
            if (s[r + 1] == 'a')
                sum++;
            r++;
        }
        if (r > n - 1)
            break;
        while(sum > m && l < r)
        {
            if (s[l] == 'a')
                sum--;
            l++;
        }
        if (sum <= m)
            ans = max(ans, r - l + 1);
        if (r >= n - 1)
            break;
        if (sum > m)
            {
                l = r + 1;
                r++;
                if (s[r] == 'a')
                    sum = 1;
                else
                    sum = 0;
            }
    }
    l = 0; r = 0; sum = 0;
    if (s[0] == 'b')
        sum++;
    while(true)
    {
        while(sum <= m && r < n)
        {
            ans = max(ans, r - l + 1);
            if (s[r + 1] == 'b')
                sum++;
            r++;
        }
        if (r > n - 1)
            break;
        while(sum > m && l < r)
        {
            if (s[l] == 'b')
                sum--;
            l++;
        }
        if (sum <= m)
            ans = max(ans, r - l + 1);
        if (r >= n - 1)
            break;
        if (sum > m)
            {
                l = r + 1;
                r++;
                if (s[r] == 'b')
                    sum = 1;
                else
                    sum = 0;
            }
    }
    printf("%d", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值