Codeforces 609D 二分 or 三分

Codeforces 509D
题目链接:
http://codeforces.com/contest/609/problem/D
题意:
有n天,m个物品,至少选k个物品,使得代价和为s。(<=1e4)
有两种支付方式,每个物品有一种支付方式。每天有不同的支付方式代价,即换取一个单位的支付方式,需要付出x[i]的代价。每个物品在这个基础上,对一个支付方式有一些单位的消耗。(<=1e6)
现在问最少到第几天,使得存在一种选物品的方式,使得代价和小于等于s。
思路:
二分是标解,没什么好说的就是二分一下答案,然后O(m)的判断下是否合法就可以。
请看我三分大法。
因为考虑对一个确定的天数,它目前的对第一种支付的最小代价a,第二种支付最小代价b,第一种支付使用的物品代价和x,第二种支付物品使用的代价和y。那么代价的函数表示就是f = a * x + b * y。
按照标解的思想排序以后,假设在第一种支付中选取k1个代价最小的物品,那么在第二种支付中选取k - k1个。
那么,容易知道x递增y递减。
然后这就是一个带有极值点的函数,很自然就用三分法取做了。
卡死51那组数据。

主要和三分法的过程有关。假设三分一段单调的区间,那么中间有两个点相等时,三分法是很容易出问题的。
所以:一、等于号的判断根据实际情况作出。比如要求这个值尽量小,那么等于时右边界也向左移。
二、特判开头结尾。

如果题目要求更苛刻一点,就可以二分答案,三分判断了~
源码:
二分答案版:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define inf (1000000007)
#define LL long long
const int MAXN = 200000 + 5;
int c1[MAXN], c2[MAXN];
int cost1[MAXN], cost2[MAXN];
int re1[MAXN], re2[MAXN];
int use[MAXN], reflag[MAXN];
int cnt1, cnt2;
LL sum1[MAXN], sum2[MAXN];
int vis[MAXN];
int n, m, k, s;
struct D
{
    int u, v;
}d1[MAXN], d2[MAXN];
bool cmp(D a, D b){return a.v < b.v;}
int solve(int u, int &mark)
{
    LL ans = s + 1;
    int t1, t2;
    t1 = min(cnt1, k), t2 = k - t1;
    mark = 0;
    while(t2 <= k && t2 <= cnt2 && t1 >= 0){
        LL temp = sum1[t1] * cost1[u] + sum2[t2] * cost2[u];
        if(ans > temp)  ans = temp, mark = t2;
        t2++, t1--;
//        t1 = max(t1, 0);
    }
//    printf("u = %d, ans = %I64d\n", u, ans);
//    system("pause");
    if(ans > s) return s + 1;
    int temp = ans;
    return temp;
}
int main()
{
    while(scanf("%d%d%d%d", &n, &m, &k, &s) != EOF){
        cost1[0] = cost2[0] = inf;
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &c1[i]);
            if(cost1[i - 1] < c1[i])    cost1[i] = cost1[i - 1], re1[i] = re1[i - 1];
            else    cost1[i] = c1[i], re1[i] = i;
        }
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &c2[i]);
            if(cost2[i - 1] < c2[i])    cost2[i] = cost2[i - 1], re2[i] = re2[i - 1];
            else    cost2[i] = c2[i], re2[i] = i;
        }
        cnt1 = cnt2 = 0;
        int u, v;
        for(int i = 1 ; i <= m ; i++){
            scanf("%d%d", &u, &v);
            if(u == 1)  d1[++cnt1].u = i, d1[cnt1].v = v;
            else    d2[++cnt2].u = i, d2[cnt2].v = v;
            reflag[i] = u;
        }

        sort(d1 + 1 , d1 + cnt1 + 1, cmp);
        sort(d2 + 1 , d2 + cnt2 + 1, cmp);
        sum1[0] = sum2[0] = 0;
        for(int i = 1 ; i <= cnt1 ; i++)    sum1[i] = sum1[i - 1] + d1[i].v;
        for(int i = 1 ; i <= cnt2 ; i++)    sum2[i] = sum2[i - 1] + d2[i].v;

        int ans;
        int le = 1, re = n;
        while(le < re - 1){
            int mid = (le + re) >> 1;
//            printf("le = %d, re = %d, solve = %d\n", le, re, solve(mid, ans));
            if(solve(mid, ans) <= s) re = mid;
            else    le = mid;
        }
        int mark;
        ans = -1;
        if(solve(le, mark) <= s)  ans = le;
        else if(solve(re, mark) <= s)   ans = re;
        if(ans == -1){
            printf("%d\n", ans);
            continue;
        }
        else{
            for(int i = 1 ; i <= m ; i++)   vis[i] = 0;
            for(int i = 1 ; i <= k - mark ; i++)    vis[d1[i].u] = 1;
            for(int j = 1 ; j <= mark ; j++)    vis[d2[j].u] = 1;
            printf("%d\n", ans);
            for(int i = 1 ; i <= m ; i++){
                if(vis[i]){
                    if(reflag[i] == 1)  printf("%d %d\n", i, re1[ans]);
                    else    printf("%d %d\n", i, re2[ans]);
                }
            }
        }
    }
    return 0;
}

三分版:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
#define inf (1000000007)
#define LL long long
const int MAXN = 200000 + 5;
struct D
{
    int u, v;
}a1[MAXN], a2[MAXN];
int x[MAXN], y[MAXN];
int costx[MAXN], costy[MAXN];   ///前i天中最小的第一支付和第二支付代价
int rex[MAXN], rey[MAXN];       ///最小支付代价的那一天
int reflag[MAXN];               ///记录第几个物品是属于第一种支付方式还是第二种
LL sum1[MAXN], sum2[MAXN];  ///前缀和
int cnt1, cnt2;             ///两种支付方式的物品数量和
int re1[MAXN], re2[MAXN];   ///记录数组
int vis[MAXN];              ///哪些物品使用过
LL check(int mark, int tx, int ty)
{
    return sum1[re1[mark]] * tx + sum2[re2[mark]] * ty;
}
bool cmp(D a, D b){return a.v < b.v;}
int main()
{
    int n, m, k, s;
    while(scanf("%d%d%d%d", &n, &m, &k, &s) != EOF){
        costx[0] = costy[0] = inf;
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &x[i]);
            if(x[i] > costx[i - 1]) costx[i] = costx[i - 1], rex[i] = rex[i - 1];
            else    costx[i] = x[i], rex[i] = i;
        }
        for(int i = 1 ; i <= n ; i++){
            scanf("%d", &y[i]);
            if(y[i] > costy[i - 1]) costy[i] = costy[i - 1], rey[i] = rey[i - 1];
            else    costy[i] = y[i], rey[i] = i;
        }

        int u, v;
        cnt1 = cnt2 = 0;
        for(int i = 1 ; i <= m ; i++){
            scanf("%d%d", &u, &v);
            reflag[i] = u;
            if(u == 1)  a1[++cnt1].v = v, a1[cnt1].u = i;
            else a2[++cnt2].v = v, a2[cnt2].u = i;
        }
        sort(a1 + 1, a1 + cnt1 + 1, cmp);
        sort(a2 + 1, a2 + cnt2 + 1, cmp);
        sum1[0] = sum2[0] = 0;
        for(int i = 1 ; i <= cnt1 ; i++)    sum1[i] = sum1[i - 1] + a1[i].v;
        for(int i = 1 ; i <= cnt2 ; i++)    sum2[i] = sum2[i - 1] + a2[i].v;
//        printf("check cnt1\n");
//        for(int i = 1 ; i <= cnt1 ; i++)    printf("i = %d, u = %d, v = %d\n", i, a1[i].u, a1[i].v);
//        printf("check cnt1\ncheck cnt2\n");
//        for(int i = 1 ; i <= cnt2 ; i++)    printf("i = %d, u = %d, v = %d\n", i, a2[i].u, a2[i].v);
//        printf("check cnt2\n");
        int t1, t2;
        t1 = min(k, cnt1), t2 = k - t1;
        int ans = n + 1;
        int cnt = 0;
//        if(n == 1 && m == 200000 && k == 100000 && s == 100000){
//            if(a2[cnt2].v > 1)  printf("hellohello\n");
//            if(a1[cnt1].v > 1) printf("sdafa\n");
//        }
//        printf("t1 = %d, t2 = %d\n", t1, t2);
        re1[0] = re2[0] = 0;
        while(t2 <= cnt2 && t2 <= k){
            re1[++cnt] = max(0, t1), re2[cnt] = t2;
            t1--, t2++;
        }
//        if(n == 1 && m == 200000 && k == 100000 && s == 100000)
//        printf("cnt1 = %d, cnt2 = %d, cnt = %d\n", cnt1, cnt2, cnt);
        for(int i = 1 ; i <= n ; i++){
            int le = 1, re = cnt;
            while(le < re - 1){
                int t1 = (le + re) >> 1;
                int t2 = (t1 + re) >> 1;
//                int l = (re - le + 1) / 3;
//                int t1 = le + l, t2 = re - l;
//                printf("le = %d, re = %d, t1 = %d, t2 = %d\n", le, re, t1, t2);
//                if(n == 1 && m == 200000 && k == 100000 && s == 100000)
//                    printf("ch1 = %I64d, ch2 = %I64d, le = %d, re = %d, t1 = %d, t2 = %d\n", ch1, ch2, le, re, t1, t2);
                if(check(t1, costx[i], costy[i]) >= check(t2, costx[i], costy[i]))  le = t1;
//                else if(ch1 == ch2) le = t1, re = t2;
                else    re = t2;
            }
            int mark = -1;
            if(check(le, costx[i], costy[i]) <= s)   mark = le;
            else  if(check(re, costx[i], costy[i]) <= s)  mark = re;
            else if(check(1, costx[i], costy[i]) <= s)  mark = 1;
            else if(check(cnt, costx[i], costy[i]) <= s)  mark = n;
//            if(n == 1 && m == 200000 && k == 100000 && s == 100000){
//            printf("i = %d, le = %d, re = %d, check(le) = %I64d, check(re) = %I64d\n", i, le, re, check(le, costx[i], costy[i]), check(re, costx[i], costy[i]));
//            printf("mark = %d\n", mark);}
            if(mark == -1)  continue;
            ans = i;
            for(int j = 1 ; j <= m ; j++)   vis[j] = 0;
            for(int t1 = 1 ; t1 <= re1[mark] ; t1++)    vis[a1[t1].u] = 1;
            for(int t2 = 1 ; t2 <= re2[mark] ; t2++)    vis[a2[t2].u] = 1;
            break;
        }
        if(ans > n){
            printf("-1\n");
        }
        else{
            printf("%d\n", ans);
            for(int i = 1 ; i <= m ; i++){
                if(vis[i]){
                    if(reflag[i] == 1)  printf("%d %d\n", i, rex[ans]);
                    else    printf("%d %d\n", i, rey[ans]);
                }
            }
        }
    }
    return 0;
}
### 回答1: Codeforces估分是指在Codeforces比赛中预估自己在比赛结束后所能获得的分数。这个分数是考虑根据自己的表现和其他参赛者的表现来预估的。 Codeforces分数是根据比赛中的排名和成功解决问题的数量来计算的。在每场比赛结束后,每位参赛者会根据其在比赛中的表现被分配一个分数。比赛中排名越高的参赛者获得的分数也越高,而解决更多问题的参赛者同样也能获得更多分数。 Codeforces估分有两种方法:一种是通过比赛中的实时排名来估计当前分数,另一种是通过计算比赛中已解决问题的分数来估计最终的总分。 对于第一种方法,我们可以在比赛过程中观察自己在排名榜上的位置和其他参赛者的分数。如果自己的排名越高,说明自己的分数也会越高;如果其他人的分数与自己相差较大,说明他们可能已经解决了更多的问题,因此可能获得更高的分数。 对于第二种方法,我们可以根据已经解决的问题数量来估算总分。Codeforces的比赛系统会根据每个问题的难度和重要性分配不同的分数。因此,如果我们能成功解决更多的问题,我们也将获得更高的分数。 总的来说,Codeforces估分是一个根据比赛中的排名和解决问题的数量来估计自己分数的过程。但是,由于每场比赛的题目和参赛人数不同,预估分数可能有一定的不确定性。因此,我们建议在比赛过程中持续观察排名榜和其他参赛者的情况,以及时作出调整和优化自己的策略。 ### 回答2: Codeforces是一个在线的编程竞赛平台,每个竞赛都有一定的难度,需要通过编写代码来解决各种算法和数据结构的问题。Codeforces的估分指的是根据你在竞赛中的表现得出的一个评分。 在Codeforces竞赛中,你会根据你的解题情况和提交的答案是否正确来获得分数。每个问题都有一定的分值,解决该问题可以获得该分值的分数。如果你的答案是正确的,你将获得该问题的分数;如果你的答案是错误的,你将不会获得分数。 Codeforces的估分算法是基于Elo算法改进的。Elo算法是一种用于评估竞技选手水平的算法。该算法会根据你的表现和对手的水平来决定你的分数变化。如果你击败了一个分数比你高的选手,你的分数可能会上升得更多;如果你输给一个分数比你低的选手,你的分数可能会下降得更多。 Codeforces的估分也考虑了竞赛的参与人数。如果你在一个参与人数多的竞赛中获得了好的成绩,你的分数可能会得到进一步的提升。相反,如果你在一个参与人数少的竞赛中获得了好的成绩,你的分数可能会得到更少的提升。 总的来说,Codeforces的估分是根据你的表现、对手的水平和竞赛的参与人数来计算的。通过持续参与竞赛并取得好的成绩,你的分数将会逐渐提升。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值