Experimental Educational Round: VolBIT Formulas Blitz

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

A - Again Twenty Five!

C - Lucky Numbers

B - Moore's Law

D - Hexagons!

J - Divisibility

注意[2,10]的lcm是 (2^3*3^2*5*7) ,也就是每种质因子取最高次。为2520。

F - Selection of Personnel

这个组合数 (C_{777}^7) 刚好不会溢出ll,一种防溢出的组合数的写法,先处理出分母这个比较小的,然后对于分子的每个部分都先和分母约分再乘上去,这样保证中间的结果都不会超过答案的 (C_{777}^7)

ll C(int n, int m) {
    ll up = 1, down = 1;
    for(int i = 1; i <= m; ++i)
        down *= i;
    for(int i = 1; i <= m; ++i) {
        ll tmp = (n + 1 - i);
        ll g = __gcd(tmp, down);
        tmp /= g;
        down /= g;
        up *= tmp;
    }
    return up / down;
}

R - Game

题意:有一个 (n*n) 的空白棋盘,两个玩家轮流涂黑格子,每次可以选一个与已经涂黑的格子没有公共边的格子图。无法操作的人输。求谁赢。

题解:这种题目有一个“模仿对手”的策略,易知若 (n) 为偶数,那么每次先手选择之后,后手选择对应的中心对称的位置,必然是可以的,所以最后一定会是先手输;若 (n) 为奇数,一开始先手先把正中间占了,然后又变成了可以跟着后手选中心对称的位置,最后一定是后手输。

N - Forecast

题意:求一个二次方程的两个不同的实根,保证有两个不同的实根,先输出大的,再输出小的。

题解:由于a的符号可能是负的,所以未必一定有 (x_1=frac{-b+sqrt{b^2-4ac}}{2a}>x_2=frac{-b-sqrt{b^2-4ac}}{2a}) ,需要判断然后交换。

K - Indivisibility

题意:求[1,n]内有多少个数,不被[2,10]其中任意一个数整除。

题解:易知不被[2,3,5,7]整除就是不被[2,10]的任意一个数整除,那么就是容斥原理,乘个莫比乌斯函数就可以了,注意不要漏了。

void TestCase() {
    ll n;
    scanf("%lld", &n);
    ll ans = n;
    ans -= n / 2;
    ans -= n / 3;
    ans -= n / 5;
    ans -= n / 7;
    ans += n / 6;
    ans += n / 10;
    ans += n / 14;
    ans += n / 15;
    ans += n / 21;
    ans += n / 35;
    ans -= n / 30;
    ans -= n / 42;
    ans -= n / 70;
    ans -= n / 105;
    ans += n / 210;
    printf("%lld
", ans);
}

那么这个式子看起来像什么呢?反正不是正常的莫比乌斯函数求和,求解的复杂度应该就是 2^k ,k是参与容斥的质数的个数。

H - Benches

题意:在一个 (n*n) 的棋盘上面放5个相同的棋子,要求同一行和同一列至多有一个棋子,问有多少种放法。

题解:第一次放,有 (n*n) 种放法,然后去掉这一行一列,第二次放有 ((n-1)*(n-1)) 种放法……注意最后棋子是无序的,把这个全排列除掉。

小心溢出,经过严密的计算,最终结果应该就是1e18的级别,是不会溢出的,也要像上面那样分次约分。

void TestCase() {
    ll n;
    scanf("%lld", &n);
    ll m = 120;
    ll res1 = n * n * (n - 1) * (n - 1) * (n - 2) * (n - 2);
    ll g = __gcd(res1, m);
    res1 /= g;
    m /= g;
    ll res2 = (n - 3) * (n - 3) * (n - 4) * (n - 4);
    g = __gcd(res2, m);
    res2 /= g;
    m /= g;
    printf("%lld
", res1 * res2 / m);
}

L - Cracking the Code

要注意的是,需要输出前导零。

G - Challenge Pennants

题意:有n个不同的盒子,5个相同的红球,3个相同的蓝球,每个球都要放在一个盒子里,一个盒子可以放任意个球。求多少种方法。

题解:首先把红蓝球分步,然后把5个红球的分法分类:

5、4+1、3+2、3+1+1、2+2+1、2+1+1+1、1+1+1+1+1

这些用组合数很好算。

ll C(ll n, ll m) {
    if(n < m)
        return 0;
    ll up = 1, down = 1;
    for(int i = 1; i <= m; ++i) {
        up *= (n + 1 - i);
        down *= i;
    }
    return up / down;
}
 
void TestCase() {
    int n;
    scanf("%d", &n);
    ll res1 = 0;
    res1 += C(n, 1);
    res1 += C(n, 1) * C(n - 1, 1);
    res1 += C(n, 1) * C(n - 1, 1);
    res1 += C(n, 1) * C(n - 1, 2);
    res1 += C(n, 2) * C(n - 2, 1);
    res1 += C(n, 1) * C(n - 1, 3);
    res1 += C(n, 5);
    ll res2 = 0;
    res2 += C(n, 1);
    res2 += C(n, 1) * C(n - 1, 1);
    res2 += C(n, 3);
    printf("%lld
", res1 * res2);
}

I - Parking Lot

和昨晚(2020/3/23)的Edu一样。

E - A rectangle

题意:给出一个矩形的左下角和右上角的坐标,求把这个矩形涂黑需要涂黑多少个六边形?

按这个图理解,应该是分奇偶性讨论一下。但是WA22了,是这个图有误导性。

void TestCase() {
    int x1, y1, x2, y2;
    scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    if((x1 - x2) % 2 == 0) {
        //有奇数列
        if((x1 - y1) % 2 == 0) {
            //左下角在中心
            if((x2 - y2) % 2 == 0) {
                //右上角也在中心
                ll dy = abs(y2 - y1) / 2;
                //少的列是dy
                ll dx = abs(x2 - x1) + 1;
                //有奇数列
                ll sum = (dx - 1) / 2 * dy + (dx + 1) / 2 * (dy + 1);
                printf("%lld
", sum);
                return;
            } else {
                //右上角在边界
                ll dy = abs(y2 - y1) / 2 + 1;
                //每列都是dy
                ll dx = abs(x2 - x1) + 1;
                //有奇数列
                ll sum = dx * dy;
                printf("%lld
", sum);
                return;
            }
        } else {
            //左下角在边界
            if((x2 - y2) % 2 == 0) {
                //右上角在中心
                ll dy = abs(y2 - y1) / 2 + 1;
                //每列都是dy
                ll dx = abs(x2 - x1) + 1;
                //有奇数列
                ll sum = dx * dy;
                printf("%lld
", sum);
                return;
            } else {
                //右上角也在边界
                ll dy = abs(y2 - y1) / 2;
                //少的列是dy
                ll dx = abs(x2 - x1) + 1;
                //有奇数列
                ll sum = (dx - 1) / 2 * (dy + 1) + (dx + 1) / 2 * dy;
                printf("%lld
", sum);
                return;
            }

        }
    } else {
        //有偶数列
        if((x1 - y1) % 2 == 0) {
            //左下角在中心
            if((x2 - y2) % 2 == 0) {
                //右上角也在中心
                ll dy = abs(y2 - y1) / 2 + 1;
                //每列都是dy
                ll dx = abs(x2 - x1) + 1;
                //有偶数列
                ll sum = dx * dy;
                printf("%lld
", sum);
                return;
            } else {
                //右上角在边界
                ll dy = abs(y2 - y1) / 2;
                //少的列是dy
                ll dx = abs(x2 - x1) + 1;
                //有偶数列
                ll sum = dx / 2 * dy + dx / 2 * (dy + 1);
                printf("%lld
", sum);
                return;
            }
        } else {
            //左下角在边界
            if((x2 - y2) % 2 == 0) {
                //右上角在中心
                ll dy = abs(y2 - y1) / 2;
                //少的列是dy
                ll dx = abs(x2 - x1) + 1;
                //有偶数列
                ll sum = dx / 2 * dy + dx / 2 * (dy + 1);
                printf("%lld
", sum);
                return;
            } else {
                //右上角也在边界
                ll dy = abs(y2 - y1) / 2 + 1;
                //每列都是dy
                ll dx = abs(x2 - x1) + 1;
                //有偶数列
                ll sum = dx * dy;
                printf("%lld
", sum);
                return;
            }
        }
    }
}

原来CF也有错题的,不过有人喷过了。这道题还限制了输入只有奇数列的情况。对于输入1 0 5 6,按照上图来理解应该是17个(3+4+3+4+3),但是答案却是18个(4+3+4+3+4),我表示强烈谴责。

void TestCase() {
    int x1, y1, x2, y2;
    scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
    ll dy = abs(y2 - y1) / 2;
    ll dx = abs(x2 - x1) + 1;
    ll sum = (dx - 1) / 2 * dy + (dx + 1) / 2 * (dy + 1);
    printf("%lld
", sum);
}

M - Turn

题意:要求将一张照片顺时针旋转d度。分两个步骤:顺时针旋转x次90度;顺时针或逆时针旋转nd度。要求在nd最小的前提下,最小化x。

题解:易知直接把d模360到[0,359],然后枚举旋转0次、1次、2次、3次的度数(0,90,180,270)算出与d的夹角的值,找到最小的那个输出。注意是夹角,所以并非是两者作差这么简单,比如要把0度理解为0度或者360度。或者说,算出两个[0,359]的角的差的绝对值abs之后,还要取min(abs,360-abs)才是夹角。

void TestCase() {
    ll d;
    scanf("%lld", &d);
    d %= 360;
    if(d < 0)
        d += 360;
    int ans = INF, cnt = 0;
    for(int i = 0; i < 4; ++i) {
        int nd = abs(d - 90 * i);
        if(min(nd, 360 - nd) < ans) {
            ans = min(nd, 360 - nd);
            cnt = i;
        }
    }
    printf("%d
", cnt);
}

Q - Pyramids

题意:给出所有边都相等的正三棱锥、正四棱锥、正五棱锥的棱长,求体积。

题解:由底面的正n边形的棱长可以算出正n边形对应的外接圆的半径。方法是:从底面圆心向各个顶点连线,划分成若干个全等的等腰三角形,可以轻松计算出等腰三角形的底角的大小(通过顶角是圆心角,或者内角和公式 (pi(n-2)) 都可以算出来),然后用棱长的一半,除以这个底角的cos值,就得到了底面外接圆的半径。得到了底面外接圆的半径,联系棱长,就可以求出这个棱锥的高(勾股定理)。最后要求底面正多边形的面积,这个可以直接计算出圆心角,然后用三角形面积公式 (frac{1}{2}sin alpha r^2) 算出每个等腰三角形的面积。最后体积要记得除以3。也就是 (V_{锥}=frac{1}{3}S_{底}h)

const double PI = acos(-1.0);

double calc_r(double l, int n) {
    double s = PI * (n - 2);
    double d = (s / n) / 2.0;
    return (l / 2.0) / cos(d);
}

double calc_s(double r, int n) {
    double s = 0.5 * sin(2.0 * PI / n) * r * r;
    return s * n;
}

void TestCase() {
    double l3, l4, l5;
    scanf("%lf%lf%lf", &l3, &l4, &l5);

    double r3 = calc_r(l3, 3);
    double h3 = sqrt(l3 * l3 - r3 * r3);
    double s3 = calc_s(r3, 3);

    double r4 = calc_r(l4, 4);
    double h4 = sqrt(l4 * l4 - r4 * r4);
    double s4 = calc_s(r4, 4);

    double r5 = calc_r(l5, 5);
    double h5 = sqrt(l5 * l5 - r5 * r5);
    double s5 = calc_s(r5, 5);

    printf("%.12f
", (s3 * h3 + s4 * h4 + s5 * h5) / 3.0);
}
原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12558009.html