2021牛客暑期多校训练营1

Posted DreamW1ngs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营1相关的知识,希望对你有一定的参考价值。

比赛地址

A(博弈+思维)

题目链接
⭐⭐⭐

题目:
Alice与Bob玩游戏,有两个石子堆石子个数分别为\\(n,m\\)。Alice先走,每人可以从一堆中拿走\\(k(k>0)\\)个,并且从另一堆拿走\\(s\\times k(s\\ge0)\\)个,不能执行操作的人为负。

解析:
引理:每一堆确定数量的石子都与唯一确定数量的另一堆石子构成必败点
证明:假定有两个必败点\\((a,b_1)(a,b_2)\\ b_1<b_2\\),则存在\\((a,b_1)\\rightarrow(a,b_2)\\),则一定有一个点不是必败点,故反证成立
因此可以维护一个\\(fail\\)数组代表必败点组合,每次可以从之前的状态\\(fail[j]\\)进行转移,而\\(i-j\\)既可以作为某个数的倍数,也可以作为一个基数,对于第一种情况,预处理好范围内的所有数的因数即可

#include<bits/stdc++.h>

using namespace std;

const int maxn = 5e3 + 1;

vector<int> fac[maxn];
int fail[maxn];
bool vis[maxn];

int main() {
	for (int i = 1; i < maxn; ++i)
		for (int j = i; j < maxn; j += i)
			fac[j].push_back(i);
	fail[0] = 0;
	for (int i = 1; i < maxn; ++i) {
		if (fail[i]) continue;
		memset(vis, 0, sizeof(vis));
		for (int j = 0; j < i; ++j) {
			if (!fail[j] && j) continue;
			for (int k = fail[j]; k < maxn; k += i - j)
				vis[k] = true;
			for (auto& i : fac[i - j]) {
				if (fail[j] + i >= maxn) break;
				vis[fail[j] + i] = true;
			}
		}
		for (int j = i + 1; j < maxn; ++j)
			if (!vis[j]) {
				fail[i] = j;
				fail[j] = i;
				break;
			}
	}
	int T;
	scanf("%d", &T);
	int a, b;
	while (T--) {
		scanf("%d%d", &a, &b);
		printf("%s\\n", fail[a] == b ? "Bob" : "Alice");
	}
}

B(计算几何)

题目链接

题目:
如给定图,求出圆心距离底部的距离

解析:
平面几何题目,构造一个相似三角形即可

#include<bits/stdc++.h>
 
using namespace std;
 
double r, a, b, h;
 
int main() {
    scanf("%lf%lf%lf%lf", &r, &a, &b, &h);
    if (2 * r < b) printf("Drop");
    else {
        printf("Stuck\\n");
        printf("%.10f", (sqrt(((a - b) / 2) * ((a - b) / 2) + h * h) * r / h - b / 2) * h / ((a - b) / 2));
    }
}

D(水题)

题目链接

题目:
给出一个\\(n\\times n\\)的01矩阵,找出\\(1\\times m\\)值均为 \\(0\\) 的子矩阵个数

解析:
统计每行长度超过\\(m\\)的连续 \\(0\\) 的个数的和

#include<bits/stdc++.h>
using namespace std;
 
char t[2005][2005];
 
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++i) {
        scanf("%s", t[i]);
    }
    scanf("%*s");
    int ans = 0, cnt;
    for (int i = 0; i < n; ++i) {
        int j = 0;
        while (j < n) {
            cnt = 0;
            while (t[i][j] == \'0\' && j < n) ++j, ++cnt;
            if (t[i][j] == \'1\') ++j;
            ans += max(cnt - m + 1, 0);
        }
    }
    printf("%d", ans);
}

F(思维)

题目链接

题目:
如果一个数,如果对应字符串的含有连续子串所对应的数是\\(3\\)的倍数,则认为这个数是\\(3-friendly\\)的数,现在给定区间\\([L, R]\\),求出区间内\\(3-friendly\\)的数的个数

解析:
定义\\(f(x)\\)代表小于等于\\(x\\)\\(3-friendly\\)的数的个数,那么答案即为\\(f(R)-f(L-1)\\)
对于任意大于三位的数而言,组成他们的数位模3后只有\\(0,1,2\\)三种可能,则必定含有子串满足\\(3-friendly\\)(根据三的倍数必定满足各位相加之和仍然位三的倍数)
那么只需要对小于等于100的数进行特判处理即可

#include<bits/stdc++.h>
using namespace std;

bool check(int x) {
	int t[3] = { 0 };
	while (x) {
		++t[x % 10 % 3];
		x /= 10;
	}
	if (t[1] || t[2]) return (t[1] > 0 && t[2] > 0) || t[0] > 0;
	return true;
}

int t[100];

int main() {
	long long L, R;
	int T;
	for (int i = 1; i < 100; ++i)
		t[i] = t[i - 1] + check(i);
	scanf("%d", &T);
	while (T--) {
		scanf("%lld%lld", &L, &R);
		--L;
		if (L < 100) L = t[L];
		else L = L - 99 + t[99];
		if (R < 100) R = t[R];
		else R = R - 99 + t[99];
		printf("%lld\\n", R - L);
	}
}

G(贪心+思维+绝对值)

题目链接
⭐⭐⭐⭐

题目:
给出长度为\\(N\\)\\(A,B\\)数组,现在可以任意更换\\(A\\)数组中元素的位置,求\\(\\sum_{i=1}^n|A_i-B_i|\\)的最大值

解析:
可以把绝对值拆开,这样就变成了\\(\\sum_{i=1}^n (\\pm A_i)+(\\pm B_i)\\),即A中正号出现次数要等与B中负号出现次数,反之亦然。假设可以交换任意次,只要挑选\\(A,B\\)中最大前\\(n\\)个加正号
那么现在如果只能交换\\(k\\)次,考虑最大利益的贪心交换。不难发现对于两队值\\((a_1,b_1)(a_2,b_2)\\),如果是\\(\\max(a_1,b_1)<\\min(a_2,b_2)\\),即两个线段没有交集,此时的贡献是\\(|b_2-a_2|+|b_1-a_1|\\),倘若交换\\(a_1,a_2\\)位置,则此时贡献为\\(|b_2-a_1|+|a_2-b_1|\\),(画出线段图很容易发现)相比于前者多了\\(2(\\max(a_1,b_1)-\\min(a_2,b_2))\\)
那么就可以找出每一对的最大值和最小值,并将他们的最大值升序排列,最小值降序排序,找出最大的前\\(k\\)对,对应的操作自然也是将最大值与最小值对应的原元素进行交换

#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
ll a[maxn], b[maxn];
ll mn[maxn], mx[maxn];
 
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; ++i)
        scanf("%lld", a + i);
    for (int i = 0; i < n; ++i)
        scanf("%lld", b + i);
 
    ll ans = 0;
    if (n == 2) {
        if (k & 1) swap(a[0], a[1]);
        ans += abs(a[0] - b[0]) + abs(a[1] - b[1]);
    }
    else {
        k = min(k, n);
        for (int i = 0; i < n; ++i)
            ans += abs(a[i] - b[i]), mx[i] = max(a[i], b[i]), mn[i] = min(a[i], b[i]);
        sort(mx, mx + n), sort(mn, mn + n, greater<ll>());
        for (int i = 0; i < k && mn[i] - mx[i]>0; ++i)
            ans += 2 * (mn[i] - mx[i]);
    }
    printf("%lld", ans);
}

I(dp+前缀和优化)

题目链接
⭐⭐⭐⭐

题目:
Alice和Bob玩游戏,每个人可以轮流从P数组中选取元素(P数组为\\(1\\sim N\\)的排列),但必须要满足以下规则

  1. 每个人取的数都必须大于之前二人取过的所有数
  2. 每个人取的数的下标都得大于之前自己取过的所有下标

如果有多种选择,则概率均匀分布,有一方无法进行操作时游戏结束
询问二人总操作数的期望(答案模取\\(998244353\\)

解析:
首先暴力状态转移,一定是一个\\(O(n^3)\\)的做法,所以考虑用前缀和进行优化降维
考虑设置\\(dp[i][j]\\),代表Alice取下标为\\(i\\)的元素时,Bob取下表为\\(j\\)元素时的期望,那么对于当前这个状态,需要判断\\(P[i]\\)\\(P[j]\\)的大小关系,谁大证明谁刚操作过,当前轮到另一方操作,那么可以得到下列状态转移方程

\\[dp[i][j]=1+\\frac{sum[i/j]}{cnt[i/j]} \\]

\\(sum,cnt\\)分别代表当前状态可以达到状态的\\(dp\\)前缀和,以及状态的数量,由于是概率均匀分布所以可以采用前缀和进行优化,他们也不难利用倒序迭代去维护

注意:
除了最后一次\\((0,0)\\)的状态,其他\\(i=j\\)的状态均不合法。

#include <bits/stdc++.h>

constexpr int maxn = 5e3 + 5;
constexpr int mod = 998244353;
int p[maxn];
int inv[maxn], cnt[maxn], dp[maxn][maxn], sum[maxn];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &p[i]);
    inv[1] = 1;
    for (int i = 2; i <= n; ++i) 
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
    for (int i = n; i >= 0; --i) {
        int cnt2 = 0, sum2 = 0;
        for (int j = n; j >= 0; --j) {
            if (i != 0 && i == j) continue;
            if (p[i] > p[j]) {
                if (!cnt2) dp[i][j] = 0;
                else dp[i][j] = (1 + 1ll * inv[cnt2] * sum2) % mod;
                cnt[j] += 1;
                (sum[j] += dp[i][j]) %= mod;
            }
            else {
                if (!cnt[j]) dp[i][j] = 0;
                else dp[i][j] = (1 + 1ll * inv[cnt[j]] * sum[j]) % mod;
                cnt2 += 1;
                (sum2 += dp[i][j]) %= mod;
            }
        }
    }
    printf("%d", dp[0][0]);
}
努力变成更好的自己把!

以上是关于2021牛客暑期多校训练营1的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营1 - F - Find 3-friendly Integers - 题解

2021牛客暑期多校训练营1 - D - Determine the Photo Position - 题解

2021牛客暑期多校训练营1 - A - Alice and Bob - 题解

2021牛客暑期多校训练营1 - A - Alice and Bob - 题解

2021牛客暑期多校训练营2,签到题CDFKI

2021牛客暑期多校训练营1