zoukankan      html  css  js  c++  java
  • 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的数量也不同,加这个优化估计是得不偿失。

  • 相关阅读:
    ASP.NET Core 返回 Json DateTime 格式
    IdentityServer4 SigningCredential(RSA 证书加密)
    IdentityServer4 指定角色授权(Authorize(Roles="admin"))
    ASP.NET Core 指定环境发布(hosting environment)
    ASP.NET Core 使用 Redis 客户端
    IdentityServer4 实现 OAuth 2.0(密码模式
    IdentityServer4 登录成功后,跳转到原来页面
    IdentityServer4 退出登录后,跳转到原来页面
    C#面向插件级别的软件开发
    使用XML序列化实现系统配置
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12264559.html
Copyright © 2011-2022 走看看