Codeforces Round #339 (Div. 2)

题目链接:https://codeforces.com/contest/614

A - Link/Cut Tree

这题目的title有点吓人啊。

题意:给一个区间 ([l,r](1leq l leq r leq 10^{18})) 和一个数 (k(1leq k leq 10^9)) ,问 ([l,r])(k) 的幂分别是。

小心溢出。

ll ans[1005];
int atop;

void test_case() {
    ll l, r, k, p = 1;
    scanf("%lld%lld%lld", &l, &r, &k);
    atop = 0;
    while(p <= r) {
        if(p >= l)
            ans[++atop] = p;
        if(r / p < k)
            break;
        else
            p *= k;
    }
    if(atop == 0)
        puts("-1");
    else {
        for(int i = 1; i <= atop; ++i)
            printf("%lld%c", ans[i], " 
"[i == atop]);
    }
}

B - Gena's Code

题意:给 (n(1leq n leq 10^5)) 个数字,其中至少 (n-1) 个数字满足只有最高位是1,其他位都是0。求他们的乘积。

题解:找出那个可能存在的与众不同的数,在它后面加上0。

char s[100005];
char t[100005];

void test_case() {
    int n;
    scanf("%d", &n);
    int sum0 = 0, mett = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%s", s);
        int l = strlen(s);
        int suc = 1, cnt1 = 0, cnt0 = 0;
        for(int j = 0; j < l; ++j) {
            if(s[j] == '1') {
                ++cnt1;
                if(cnt1 >= 2) {
                    suc = 0;
                    break;
                }
            } else if(s[j] != '0') {
                suc = 0;
                break;
            } else
                ++cnt0;
        }
        if(cnt0 == l) {
            puts("0");
            return;
        }
        if(!suc) {
            strcpy(t, s);
            mett = 1;
        } else
            sum0 += cnt0;
    }
    if(mett)
        printf("%s", t);
    else
        putchar('1');

    while(sum0--)
        putchar('0');
    putchar('
');
}

C - Peter and Snow Blower

题意:一台凸多边形的除雪机,凸多边形按逆时针顺序给出,把它栓在一根柱子P上,然后它会绕着柱子旋转,把覆盖到的位置的雪擦除。问擦除的雪的面积。

题解:显然擦除的面积是一个圆环,且外沿必定是离P最远的凸多边形的顶点,但是内沿有可能是凸多边形的某条边绕P旋转一次得到的。所以求出每条边到P的距离,取最小值就可以得到内沿半径。最后根据 (S=pi R^2) 求出圆的面积然后作差。

突然发现没有计算几何的模板。

double x[100005], y[100005];

struct Point {
    double x, y;
    Point (double xx = 0, double yy = 0) {
        x = xx;
        y = yy;
    }
    friend Point operator-(const Point &a, const Point &b) {
        return Point(a.x - b.x, a.y - b.y);
    }
    double norm() {
        return sqrt(x * x + y * y);
    }
};

int cmp(double x) {
    if(x > 1e-9)
        return 1;
    if(x < 1e-9)
        return -1;
    return 0;
}

double dot(Point a, Point b) {
    return a.x * b.x + a.y * b.y;
}

double det(Point a, Point b) {
    return a.x * b.y - a.y * b.x;
}

double dist(Point a, Point b) {
    return (a - b).norm();
}


double dis_point_segment(const Point p, const Point s, const Point t) {
    if(cmp(dot(p - s, t - s)) < 0)
        return (p - s).norm();
    if(cmp(dot(p - t, s - t)) < 0)
        return (p - t).norm();
    return fabs(det(s - p, t - p)) / dist(s, t);
}

void test_case() {
    int n;
    double X, Y;
    scanf("%d%lf%lf", &n, &X, &Y);
    double R = 0, r = 1e18;
    for(int i = 1; i <= n; ++i) {
        scanf("%lf%lf", &x[i], &y[i]);
        R = max(R, (X - x[i]) * (X - x[i]) + (Y - y[i]) * (Y - y[i]));
    }
    R = sqrt(R);
    Point O(X, Y);
    for(int i = 2; i <= n; ++i)
        r = min(r, dis_point_segment(O, Point(x[i], y[i]), Point(x[i - 1], y[i - 1])));
    r = min(r, dis_point_segment(O, Point(x[1], y[1]), Point(x[n], y[n])));
    printf("%.12f
", (R * R - r * r)* acos(-1.0));
}

后面写了自己的计算几何模板之后可以用来验证一下。

*D - Skills

很有收获的一道题,提醒自己要考虑前后步骤的影响。

题意:一个游戏角色,有 (n(1leq n leq 200000)) 种属性值,每种属性值都是同一个上限 (A(1leq A leq 10^9)) 。游戏角色的价值取决于其“练满”的属性的个数和最低的属性值的值。公式为 (value = (c_f*sumlimits_{i=1}^{n}[a_i=A])+(c_m*minlimits_{i=1}^{n}a_i)) 。现在你可以加 (m) 个点,最大化你的角色的价值。

题解:很简单的一个贪心策略,排序之后肯定往两头点,可以枚举点满的属性的个数,剩下的点全部用来提升最小值。但是实现的时候需要注意一些细节。考虑一种分配方式,要求使得分配结果最小值为M,那么先把<M的全部提升到M,然后再贪心把最大的一些值提升到A,注意这里有可能会把M的值提升到A!一开始的实现就错在这里,忽略了两边的互相影响。由这个观察可知A的来源一定是本来就比较大的这些值,而最小值的来源不能是已经被记为A的值(为这些本身<M但是提升到A的属性再付费一次变成M就错了)。复杂度 (O(nlognlogA)) 但是应该跑不满。

总结:注意前后步骤之间的互相影响。

int n, A, cf, cm;
ll m;

pii a[100005];
int aa[100005];
ll prefix[100005];

bool check(int minval, ll resm, int cn) {
    int pos = upper_bound(aa + 1, aa + 1 + cn, minval) - aa - 1;
    if(1ll * pos * minval - prefix[pos] > resm)
        return false;
    return true;
}

int bs(ll resm, int cn) {
    int L = aa[1], R = A;
    while(1) {
        int M = (L + R) >> 1;
        if(L == M) {
            if(check(R, resm, cn))
                return R;
            assert(check(L, resm, cn));
            return L;
        }
        if(check(M, resm, cn))
            L = M;
        else
            R = M - 1;
    }
}

ll calc_ans(int cntA) {
    if(1ll * A * cntA - (prefix[n] - prefix[n - cntA]) > m)
        return -LINF;
    ll resm = m - (1ll * A * cntA - (prefix[n] - prefix[n - cntA]));
    ll tmpans = 1ll * cntA * cf;
    int minval = bs(resm, n - cntA);
    tmpans += 1ll * minval * cm;
    return tmpans;
}

int ans[100005];

void gen_ans(int cntA) {
    if(1ll * A * cntA - (prefix[n] - prefix[n - cntA]) > m)
        exit(-1);
    ll resm = m - (1ll * A * cntA - (prefix[n] - prefix[n - cntA]));
    ll minval = bs(resm, n - cntA);
    for(int i = 1; i <= n; ++i) {
        if(a[i].first < minval)
            ans[a[i].second] = minval;
        else
            ans[a[i].second] = a[i].first;
    }
    for(int i = n; cntA--; --i)
        ans[a[i].second] = A;
    for(int i = 1; i <= n; ++i)
        printf("%d%c", ans[i], " 
"[i == n]);
}

void test_case() {
    scanf("%d%d%d%d%lld", &n, &A, &cf, &cm, &m);
    ll sumx = 0;
    for(int i = 1, x; i <= n; ++i) {
        scanf("%d", &x);
        a[i] = {x, i};
        aa[i] = x;
        sumx += x;
    }
    if(1ll * A * n - sumx <= m) {
        ll ans = 1ll * n * cf + 1ll * A * cm;
        printf("%lld
", ans);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", A, " 
"[i == n]);
        return;
    }
    sort(a + 1, a + 1 + n);
    sort(aa + 1, aa + 1 + n);
    for(int i = 1; i <= n; ++i)
        prefix[i] = prefix[i - 1] + aa[i];

    ll ans = -LINF;
    int cntA = -1;
    for(int i = 0; i < n; ++i) {
        ll tmpans = calc_ans(i);
        if(tmpans > ans) {
            ans = tmpans;
            cntA = i;
        }
    }
    printf("%lld
", ans);
    gen_ans(cntA);
}

思考:假如改变枚举cntA的顺序,是否可以缩小bs那次二分的范围呢?现在是A越买越多,虽然硬币越来越少但是需要提升到最小值M的属性的数量也有可能下降。不过由于cntA的数量不同导致需要提升为M的数量也不同,加这个优化估计是得不偿失。

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12264559.html