VP Educational Codeforces Round 13


A. Johny Likes Numbers

题意:找出比\(n\)大的最小的\(k\)的倍数。

求出\(n\)\(k\)的几倍后加一乘\(k\)

点击查看代码
void solve() {
    int n, k;
    std::cin >> n >> k;
    std::cout << (n / k + 1) * k << "\n";
}


B. The Same Calendar

题意:给出当前年份,求下一个每天星期数都相等的年份。

首先只有两个年份天数相同才可能,然后考虑枚举,发现\(365 \% 7 = 1, 366 \% 7 = 2\),那么当是闰年就加2,不是就加1,当这个加出来的数是7的倍数就是答案。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    auto check = [&](int n) -> int {
    	return n % 400 == 0 || (n % 4 == 0 && n % 100 != 0);
    };

    int x = check(n);
    int t = 0;
    do {
    	++ n;

    	if (check(n)) {
    		t += 366;
    	} else {
    		t += 365;
    	}

    } while (t % 7 || check(n) != x);

    std::cout << n << "\n";
}

C. Joty and Chocolate

题意:在\([1, n]\)这些数里,你可以给\(a\)的倍数每个加\(p\)的价值,给\(b\)的倍数每个加\(q\)的价值,每个数只能用一次,求最大价值。

显然我们应该让价值大的染更多的色,并且给更多数染色,那么先给\(a, b\)的倍数都算一遍,然后减去它们重复的倍数的价值就行。

点击查看代码
void solve() {
    i64 n, a, b, p, q;
    std::cin >> n >> a >> b >> p >> q;
    if (p < q) {
    	std::swap(a, b);
    	std::swap(p, q);
    }

    i64 ans = 0;
    ans += n / a * p + n / b * q - n / (a / std::gcd(a, b) * b) * q;
    std::cout << ans << "\n";
}

D. Iterated Linear Function

题意:一个递归函数:\(f(x) = Ax + B, g(n, x) = f(g(n - 1, x)) n > 0; g(n, x) = x, n = 0\)。求\(g(n, x)\)

模拟一下,\(g(0, x) = x, g(1, x) = Ax + B, g(2, x) = A^2x + AB + B, g(3, x) = A^3x + A^2B + AB + B\),那么发现最终\(x\)的系数是\(A^n\),然后后面是\(\sum_{i=0}^{n-1} A^i \times B\),发现这是一个等比数列,用等比数列求和公式计算即可。

点击查看代码
void solve() {
    i64 A, B, n, x;
    std::cin >> A >> B >> n >> x;
    if (A == 1) {
    	std::cout << x + (Z)n * B << "\n";
    	return;
    }

    //A^n * x + A^n-1 * B + A^n-2 * B.. A^0 * B
    Z ans = power<Z>(A, n) * x + (1 - power<Z>(A, n)) / (Z)(1 - A) * B;
    std::cout << ans << "\n";
}

E. Another Sith Tournament

题意:有\(n\)个人要比赛,每场比赛两两对决,第\(i\)个人有\(p[i][j]\)几率战胜第\(j\)个人。你是第一个人,你可以选择第一个上场的人,已经在每场比赛后选一个没上场的人替换失败的人。

\(n <= 18\),自然想到状压\(dp\),但状态转移有点说法,如果正着做,记\(f[s][i]\)为当前上场过的集合为\(s\),剩下\(i\)的获胜概率,发现转移方程好想,但算出来答案不对,因为有些状态的值不好确定,如果集合有过第一个人,但获胜者不是第一个,概率应该为0,但这样就打乱我们的转移过程。
考虑反着来,\(f[s][i]\)表示\(i\)在擂台上,\(s\)集合的人都可以上场,那么倒着来,\(f[1][0] = 1\),然后对于每个\(f[s][i]\),选一个未上场的\(j\)来和\(i\)打,就有两种结果,取最大值就可以,状态转移方程为\(f[s][i] = \max(f[s][i], f[s - (1 << j)][i] \times p[i][j] + f[1 - (1 << i)][j] \times f[j][i])\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector p(n, std::vector<double>(n));
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		std::cin >> p[i][j];
    	}
    }

    std::vector f(1 << n, std::vector<double>(n));
    f[1][0] = 1;

    for (int i = 2; i < 1 << n; ++ i) {
    	for (int j = 0; j < n; ++ j) {
    		if (i >> j & 1) {
    			for (int k = 0; k < n; ++ k) {
    				if ((i >> k & 1) && j != k) {
    					f[i][j] = std::max(f[i][j], f[i - (1 << k)][j] * p[j][k] + f[i - (1 << j)][k] * p[k][j]);
    				}
    			}
    		}
    	}
    }

    double ans = 0;
    for (int i = 0; i < n; ++ i) {
    	ans = std::max(ans, f[(1 << n) - 1][i]);
    }

    std::cout << std::fixed << std::setprecision(12);
    std::cout << ans << "\n";
}
posted @ 2025-02-22 18:51  maburb  阅读(22)  评论(0)    收藏  举报