牛客周赛 Round 77

A. 时间表

输出即可。

点击查看代码
void solve() {
	std::string s[] = {"20250121", "20250123", "20250126", "20250206", "20250208", "20250211"};
    int n;
    std::cin >> n;
    std::cout << s[n - 1] << "\n";
}

B. 数独数组

题意:一个数组的任意一个长度为\(9\)的连续子数组都是\(1\)\(9\)的排列。有没有一种摆放方式符合。

我们应该\(\{1, 2, ..., 8, 9, 1, 2 ...\}\)这样循环摆下去,才能保证最多的子数组满足,那么每个数组的出现次数要么是\(\lfloor \frac{n}{9} \rfloor\),要么是\(\lceil\frac{n}{9} \rceil\)

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

    for (int i = 1; i <= 9; ++ i) {
    	if (cnt[i] != n / 9 && cnt[i] != (n + 8) / 9) {
    		std::cout << "NO\n";
    		return;
    	}
    }

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

C. 小红走网格

题意:一个网格里,每次分别可以往上下左右走对应的\(a, b, c, d\)步,问能不能到\((x, y)\)

其实是\(k_1a + k_2b = y, k_3c + k_4d = x\),那么要求\(gcd(a, b) | y\)\(gcd(c, d) | x\)

点击查看代码
void solve() {
    int x, y, a, b, c, d;
    std::cin >> x >> y >> a >> b >> c >> d;
    if (y % std::gcd(a, b) == 0 && x % std::gcd(c, d) == 0) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

D. 隐匿社交网络

题意:\(n\)个数,如果两个数之间二进制下有一位上都是\(1\),那么他们就是一个集合的,问最大的集合有多少人。

明显是并查集,以每一位为一个集合,对应每个数合并其所有\(1\)的位的集合就行。

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

    std::vector<int> fa(60), cnt(60, 0);
    std::iota(fa.begin(), fa.end(), 0);

    std::function<int(int)> find = [&](int x) -> int {
    	return x == fa[x] ? x : fa[x] = find(fa[x]);
    };

    for (int i = 0; i < n; ++ i) {
    	i64 x = -1;
    	for (i64 j = 0; j < 60; ++ j) {
    		if (a[i] >> j & 1) {
    			if (x == -1) {
    				x = j;
    			} else {
    				if (find(x) == find(j)) {
    					continue;
    				}
    				cnt[find(x)] += cnt[find(j)];
    				fa[find(j)] = find(x);
    			}
    		}
    	}

    	cnt[find(x)] += 1;
    }

    int ans = 0;
    for (int i = 0; i < 60; ++ i) {
    	if (find(i) == i) {
    		ans = std::max(ans, cnt[i]);
    	}
    }
    std::cout << ans << "\n";
}

E. 1or0

题意:一个\(01\)串,每次问一个区间,回答这个区间有多少个子区间至少有\(1\)\(1\)

我们记\(last_i\)为左边离\(i\)最近的\(1\)的位置,\(sum_i = \sum_{j=1}^{i} last_i\)。记\(suf_i\)为右边离\(i\)最近的\(1\)的位置。
那么当询问一个区间\([l, r]\)时,我们可得\(sum_r - sum_{l-1}\),但这个计算得答案区间左端点会在\(l\)左边,所以我们要减去这种情况,那么看\(last_l\)\(suf_l\)在哪,那么\(last_i\)\(l\)之后的有\(r - l + 1 - (suf_l - l)\)个,在\(l\)之前的有\(suf_l - l\)个,要减去的贡献为\((r-l+1-(suf_l1-l))*(l-1)+(suf_l-l)*last_l\)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::string s;
    std::cin >> s;
    std::vector<i64> sum(n + 1), last(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	if (s[i - 1] == '1') {
    		last[i] = i;
    	} else {
    		last[i] = last[i - 1];
    	}

    	sum[i] = sum[i - 1] + last[i];
    }

    std::vector<i64> suf(n + 2, n + 1);
    for (int i = n; i >= 1; -- i) {
    	if (s[i - 1] == '0') {
    		suf[i] = suf[i + 1];
    	} else {
    		suf[i] = i;
    	}
    }

    int q;
    std::cin >> q;
    while (q -- ) {
    	i64 l, r;
    	std::cin >> l >> r;
    	i64 ans = sum[r] - sum[l - 1];
    	i64 L = last[l], R = std::min(r + 1, suf[l]);
    	ans -= ((i64)r - l + 1 - (R - l)) * (l - 1);
    	ans -= ((i64)R - l) * L;
    	std::cout << std::max(0ll, ans) << "\n";
    }
}

F. 计树

题意:一棵树,会随机从集合里选两个点,问每个点可能是这两个点的\(lca\)的情况有多少种。

考虑树形\(dp\)\(f_u\)表示答案,\(cnt_u\)表示以\(u\)为根的子树下有多少个可选点。那么一个让\(u\)每个子树单独考虑,只要两个点在不同的子树下那么\(lca\)就是\(u\),那么答案是让每个子树的可选点两两相乘,我们知道\(\sum_{i=1}^{n}\sum_{j=1}^{n} a_ia_j = (\sum_{i=1}^{n} a_i)^2\),那么我们要求的答案就是\((\sum_{v \in u} cnt_v)^2 - \sum_{v \in u} cnt_v^2\),如果\(u\)本身就是可选点,还有加上\(\sum_v cnt_v\)以及加上两个点都选自己的情况。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::vector<int> > adj(n);
    for (int i = 1; i < n; ++ i) {
    	int u, v;
    	std::cin >> u >> v;
    	-- u, -- v;
    	adj[u].push_back(v);
    	adj[v].push_back(u);
    }

    int k;
    std::cin >> k;
    std::vector<int> a(n);
    for (int i = 0; i < k; ++ i) {
    	int x;
    	std::cin >> x;
    	-- x;
    	a[x] = 1;
    }

    std::vector<i64> f(n), cnt(n);
    auto dfs = [&](auto self, int u, int fa) -> void {
    	i64 sum = 0;
    	for (auto & v : adj[u]) {
    		if (v == fa) {
    			continue;
    		}

    		self(self, v, u);
    		cnt[u] += cnt[v];
    		sum += cnt[v] * cnt[v];
    	}

    	f[u] = (cnt[u] * cnt[u] - sum) + cnt[u] * a[u] * 2 + a[u];
    	cnt[u] += a[u];
    };

    dfs(dfs, 0, -1);
    for (int i = 0; i < n; ++ i) {
    	std::cout << f[i] << " \n"[i == n - 1];
    }
}
posted @ 2025-01-19 21:42  maburb  阅读(86)  评论(0)    收藏  举报