kuangbin题单|数学训练一

数学训练(×)

脑筋急转弯\((\checkmark)\)

1.LightOJ1008 Fibsieve's Fantabulous Birthday 找规律

此题已经写过题解了,看@此处

2.LightOJ1010 Knights in Chessboard 找规律

没想出来,分享好题解@此处

总结就是讨论\(min(n,m)\)的取值,如果大于\(2\),直接取棋盘的黑色部分。

如果等于\(2\),将棋盘划分为若干个\(2*2\)的田,剩下的部分长度只能为\(3,2,1\)\(3\)的情况就是一个田加上一个空白区域,\(2\)的情况就是一个田,\(1\)的情况就是一个长条。

如果等于\(1\),显然每一块都可以放一个骑士,答案为\(m\)

void solve(int kase) {
    cin >> m >> n;
    if (n > m) swap(n, m);
    if (n > 2) {
        printf("Case %d: %d\n", kase, (n * m + 1) / 2);
        return;
    } else if (n == 2) {
        int t = 0;
        if (m % 4 == 3 || m % 4 == 2) t = 2;
        else if (m % 4 == 1) t = 1;
        else t = 0;
        printf("Case %d: %d\n", kase, 4 * (m / 4) + 2 * t);
        return;
    } else printf("Case %d: %d\n", kase, m);

}

3.LightOJ1020 A Childhood Game 巴什博弈

本题是\(m=2\)情况下的巴什博弈。

对于\(n\)个棋子,每人每次可以拿\(1\sim m\)个:

  1. 如果取最后子判胜,那么当\(n\%(m+1)==0\)时后手必胜,否则先手必胜。

简单证明:

无论先手取多少子,后手都可以取一定的量使两者和为\((m+1)\)(比如先手取\(m\)个,后手则可以取\(1\)个)。这使得取子变为\(\frac{n}{m+1}\)轮和最后一轮。若最后一轮不存在,显然是后手取最后一子,故后手必胜。

  1. 如果取最后子判负,那么当\((n-1)\%(m+1)==0\)时后手必胜,否则先手必胜。

简单证明:

取最后子判负,等价于先取完前\(n-1\)个子的获胜,也就是判断谁先取到第\(n-1\)个子,此时与第一种情况相同。

void solve(int kase) {
    string win = "";
    int m = 2;
    if (s == "Bob") {
        if (n % (m + 1) == 0) win = "Alice";
        else win = "Bob";
    } else {
        if ((n - 1) % (m + 1) == 0) win = "Bob";
        else win = "Alice";
    }
    printf("Case %d: %s\n", kase, win.c_str());
}

4.LightOJ1078 Integer Divisibility 模拟

\(digit\)\(k\),模数为\(n\),答案为\(y\)

\(y\)的初始值为\(k\),显然迭代过程是\(y\%n=(y*10+k)\%n=[(y*10)\%n+k\%n]\%n\)

循环直到\(y\%n==0\)为止。

void solve(int kase) {
    LL y = k;
    int ans = 1;
    while (y % n != 0) {
        y = (y * 10) % n + k % n;
        ans++;
    }
    printf("Case %d: %d\n", kase, ans);
}

5.LightOJ1116 Ekka Dokka 质因数分解

显然若\(w\)是奇数,一定无法分解成奇数*偶数。

继续讨论,如果\(w\)可以表示成\(2*w/2\),其中\(w/2\)是奇数,那么一定是满足最小偶数的条件的。

否则,我们对\(w\)的所有质因数进行分析。要求最小偶数等价于最大奇数,最大的奇数=所有不包括\(2\)的质因数之积,在实际代码中,只要不断\(w/2\)直到\(w\)为奇数即可。

void solve(int kase) {
    if (n & 1LL) {
        printf("Case %d: Impossible\n", kase);
        return;
    }
    if (((n >> 1LL) & 1LL)) {
        printf("Case %d: %lld %lld\n", kase, n / 2, 2);
        return;
    } else {
        LL t = n;
        while (t) {
            if (t & 1LL) break;
            t >>= 1LL;
        }
        printf("Case %d: %lld %lld\n", kase, t, n / t);
    }
}

6.LightOJ1148 Mad Counting 找规律

\(cnt[i]\)\(i\)出现的次数。我们举一个例子:1 1 1 1 1

可以发现,满足最小人数的分组是\([1\ 1\ 1],[1\ 1]\)

也就是说:对于同一个\(i\)被分为了\(\frac{cnt[i]}{i+1}\)组,如果\(cnt[i]\)能被\(i+1\)整除,\(ans+=\frac{cnt[i]}{i+1}*(i+1)\),否则\(ans+=\frac{cnt[i]}{i+1}*(i+1)+(i+1)\),即前\(\frac{cnt[i]}{i+1}\)组的人数+最后一组的人数。

void solve(int kase) {
    memset(cnt, 0, sizeof(cnt));
    cin >> n;
    int L = inf, R = -1;
    int ans = 0;
    while (n--) {
        int x;
        cin >> x;
        cnt[x]++;
        L = min(L, x);
        R = max(R, x);
    }
    for (int i = L; i <= R; i++) {
        if (cnt[i] % (i + 1) != 0) ans += (cnt[i] / (i + 1)) * (i + 1) + (i + 1);
        else ans += cnt[i];
    }
    printf("Case %d: %d\n", kase, ans);
}

7.LightOJ1179 Josephus Problem 约瑟夫环

套个递推公式就出来了,看着\(2^{31}\)怪大的其实压根没爆\(int\)

void solve(int kase) {
    cin >> n >> k;
    int ans = 0;
    for (int i = 1; i <= n; i++) ans = (ans + k) % i;
    printf("Case %d: %d\n", kase, ans + 1);
}

8.LightOJ1275 Internet Service Providers 小学数学

呃,一元二次方程。

int count(int x) {
    return -n * x * x + c * x;
}

void solve(int kase) {
    cin >> n >> c;
    int ans = 0;
    if (!n || !c) {
        printf("Case %d: 0\n", kase);
        return;
    }
    if (!c % (2 * n)) {
        ans = c / (2 * n);
    } else {
        int temp = c / (2 * n);
        if (count(temp) < count(temp + 1)) ans = temp + 1;
        else ans = temp;
    }
    printf("Case %d: %d\n", kase, ans);
}

9.LightOJ1294 Positive Negative Sign 小学数学

易推得,每两组的和为\(m^2\),一共有\(\frac{n}{2m}\)组,所以答案是\(\frac{n}{2m}*m^2=mn/2\)

void solve(int kase) {
    cin >> n >> m;
    LL ans = n * m >> 1LL;
    printf("Case %d: %lld\n", kase, ans);
}

10.LightOJ1297 Largest Box 带精度二分

体积\(V(x)=x(l-2x)(w-2x)\)

一阶导可得:\(V'(x)=12x^2-4(l+w)x+wl\)

易知\(V'(x)\)无整数解,令\(L=0,R=w/2,eps=1e-6\)进行二分,答案带入\(V(x)\)即可。

double calc(double x) {
    return (double) (x * (w - 2 * x) * (l - 2 * x));
}

double calc2(double x) {
    return (double) (12 * x * x - 4 * (l + w) * x + w * l);
}

double solve2() {
    double l = 0, r = w / 2.0;
    double ans = 0;
    while (r - l > eps) {
        double mid = (l + r) / 2.0;
        if (calc2(mid) > 0) l = mid, ans = l;//图像可知导函数严格递减
        else r = mid - eps;
    }
    return ans;
}

void solve(int kase) {
    cin >> l >> w;
    if (w > l) swap(l, w);
    double x1 = solve2();
    double ans = calc(x1);
    printf("Case %d: %lf\n", kase, ans);
}
posted @ 2023-01-13 15:51  SxtoxA  阅读(36)  评论(0)    收藏  举报
12 13