Codeforces Round #738 (Div. 2)

A

由于与运算不会把值变得更大,我们可以考虑将一个数不断与上别的数。

比如:对于第一个数,我们可以不断利用 \([1, 1]\)\([1, 2]\)、……、\([1, n]\) 来让第一个数变成所有数与起来的值。其他位置上的数字可以通过再做一次上述操作得到所有数与起来的值。

于是答案就是所有数与起来的值。

代码:

#include <stdio.h>

int main(){
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++){
		int n, ans = 0x7fffffff;
		scanf("%d", &n);
		for (int j = 1; j <= n; j++){
			int a;
			scanf("%d", &a);
			ans &= a;
		}
		printf("%d\n", ans);
	}
	return 0;
}

B

首先,如果给定字符串全是 ?,直接 BR 交替即可;

如果给定字符串不全是 ? 但开头可能有一段 ?,找到第一个非 ? 的字符,向前交替即可,向后依然交替,直到遇到下一个 ?,向后交替即可,其他以此类推。

但这样怎么保证正确性呢?比如:B??B,如果往 ?? 中填入 RB,结果有 \(2\) 个;如果填入 BR,结果也有 \(2\) 个;

再比如:B??R,如果往 ?? 中填入 RB,结果有 \(0\) 个;如果填入 BR,结果也有 \(2\) 个。

于是在第一个 ? 前填入与第一个 ? 前相异的字母不会比其他方案更劣。

代码:

#include <stdio.h>

char s[107];

int main(){
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++){
		int n, j = 1;
		scanf("%d", &n);
		scanf("%s", &s[1]);
		s[n + 1] = 'B';
		while (j <= n && s[j] == '?') j++;
		for (int k = j - 1; k >= 1; k--){
			s[k] = s[k + 1] == 'B' ? 'R' : 'B';
		}
		for (; j <= n; j++){
			if (s[j] == '?') s[j] = s[j - 1] == 'B' ? 'R' : 'B';
		}
		for (int k = 1; k <= n; k++){
			printf("%c", s[k]);
		}
		printf("\n");
	}
	return 0;
}

C

显然,我们有三种方案:

  • 先走 \(n + 1 \to 1\),再 \(1 \to 2 \to \cdots \to n\)
  • 选定一个 \(x\),先走 \(1 \to 2 \to \cdots \to x\),再走 \(x \to n + 1 \to x + 1\),最后走 \(x + 1 \to x + 2 \to \cdots \to n\)
  • 先走 \(1 \to 2 \to \cdots \to n\),再 \(n \to n + 1\)

直接模拟每种情况即可。

代码:

#include <stdio.h>
#include <stdbool.h>

int a[100007], ans[100007];

int main(){
	int t;
	scanf("%d", &t);
	for (int i = 1; i <= t; i++){
		int n, ni;
		bool flag = false;
		scanf("%d", &n);
		ni = n + 1;
		for (int j = 1; j <= n; j++){
			scanf("%d", &a[j]);
		}
		if (a[1] == 1){
			flag = true;
			ans[1] = ni;
			for (int j = 1; j <= n; j++){
				ans[j + 1] = j;
			}
		} else {
			for (int j = 1, k = 0; j <= n; j++){
				ans[++k] = j;
				if (!flag && a[j] == 0 && (j == n || a[j + 1] == 1)){
					ans[++k] = ni;
					flag = true;
				}
			}
		}
		if (!flag){
			printf("-1\n");
		} else {
			for (int j = 1; j <= ni; j++){
				printf("%d ", ans[j]);
			}
			printf("\n");
		}
	}
	return 0;
}

D1

前置芝士:并查集

直接暴力用并查集判断每条边建了后是否会成环(即它们之前是否在一个集合里)即可。时间复杂度为 \(O(n^2 \alpha(n))\)

代码:

#include <stdio.h>

int root[7][1007], ansu[1007], ansv[1007];

inline void init(int n){
	for (register int i = 1; i <= n; i++){
		root[1][i] = root[2][i] = i;
	}
}

int get_root(int k, int x){
	if (root[k][x] == x) return x;
	return root[k][x] = get_root(k, root[k][x]);
}

inline void merge(int k, int x, int y){
	int x_root = get_root(k, x), y_root = get_root(k, y);
	if (x_root != y_root) root[k][x_root] = y_root;
}

int main(){
	int n, m1, m2, ansh = 0;
	scanf("%d %d %d", &n, &m1, &m2);
	init(n);
	for (int i = 1; i <= m1; i++){
		int u, v;
		scanf("%d %d", &u, &v);
		merge(1, u, v);
	}
	for (int i = 1; i <= m2; i++){
		int u, v;
		scanf("%d %d", &u, &v);
		merge(2, u, v);
	}
	for (int i = 1; i < n; i++){
		for (int j = i + 1; j <= n; j++){
			if (get_root(1, i) != get_root(1, j) && get_root(2, i) != get_root(2, j)){
				merge(1, i, j);
				merge(2, i, j);
				ansh++;
				ansu[ansh] = i;
				ansv[ansh] = j;
			}
		}
	}
	printf("%d\n", ansh);
	for (int i = 1; i <= ansh; i++){
		printf("%d %d\n", ansu[i], ansv[i]);
	}
	return 0;
}

E

前置芝士:莫比乌斯反演

讲个笑话:赛时我因为去搞 D2 而在最后五秒码完这道题却发现过不了样例,第二天调了三分钟就 AC 了。

原式 \(= \displaystyle\sum_{a_1 = l_1}^{r_1} \sum_{a_2 = l_2}^{r_2} \cdots \sum_{a_n = l_n}^{r_n} [\sum_{i = 1}^n a_i \leq m \operatorname{and} \gcd(a_1, a_2, \cdots, a_n) = 1]\)

$ = \displaystyle\sum_{d = 1}^m \mu(d) \sum_{a_1 = l_1, d\ |\ a_1}^{r_1} \sum_{a_2 = l_2, d\ |\ a_2}^{r_2} \cdots \sum_{a_n = l_n, d\ |\ a_n}^{r_n} [\sum_{i = 1}^n a_i \leq m]$

$ = \displaystyle\sum_{d = 1}^m \mu(d) \sum_{a_1 = \lceil \frac{l_1}{d} \rceil}^{\lfloor \frac{r_1}{d} \rfloor} \sum_{a_2 = \lceil \frac{l_2}{d} \rceil}^{\lfloor \frac{r_2}{d} \rfloor} \cdots \sum_{a_n = \lceil \frac{l_n}{d} \rceil}^{\lfloor \frac{r_n}{d} \rfloor} [\sum_{i = 1}^n a_i \leq \lfloor \frac{m}{d} \rfloor]$

后半部分直接用前缀和优化 dp 维护即可。dp 部分时间复杂度为 \(O(\frac{nm}{d})\),于是整体时间复杂度为 \(O(nm \ln m)\)

代码:

#include <stdio.h>

const int N = 1e5 + 7, M = 50 + 7, mod = 998244353;
int prime[N], mu[N], l[M], r[M], r_[M], sum_[N], sum[N], dp[M][N];
bool p[N];

inline void init(int n){
	int cnt = 0;
	p[0] = p[1] = true;
	mu[1] = 1;
	for (register int i = 2; i <= n; i++){
		if (!p[i]){
			prime[++cnt] = i;
			mu[i] = -1;
		}
		for (register int j = 1; j <= cnt && i * prime[j] <= n; j++){
			int t = i * prime[j];
			p[t] = true;
			if (i % prime[j] == 0){
				mu[t] = 0;
				break;
			}
			mu[t] = -mu[i];
		}
	}
}

inline int min(int a, int b){
	return a < b ? a : b;
}

inline int do_dp(int n, int m){
	int ans = 0;
	for (register int i = 0; i <= m; i++){
		sum_[i] = 1;
	}
	for (register int i = 1; i <= n; i++){
		for (register int j = 0; j <= m; j++){
			int t = min(r_[i], j);
			if (i == n) ans = (ans + sum_[t]) % mod;
			sum[j] = sum_[t];
			if (j > 0) sum[j] = (sum[j] + sum[j - 1]) % mod;
		}
		for (register int j = 0; j <= m; j++){
			sum_[j] = sum[j];
		}
	}
	return ans;
}

int main(){
	int n, m, mi, ans = 0;
	scanf("%d %d", &n, &m);
	mi = m + 1;
	init(m);
	for (register int i = 1; i <= n; i++){
		scanf("%d %d", &l[i], &r[i]);
	}
	for (register int i = 1; i <= m; i++){
		int sum = 0, tm = m / i;
		for (register int k = 1; k <= n; k++){
			int tl = (l[k] - 1) / i + 1;
			r_[k] = r[k] / i - tl;
			sum = min(sum + tl, mi);
		}
		if (tm >= sum) ans = ((ans + mu[i] * do_dp(n, tm - sum)) % mod + mod) % mod;
	}
	printf("%d", ans);
	return 0;
}
posted @ 2021-08-16 09:13  LovelyLeasier  阅读(66)  评论(0)    收藏  举报