题目
A
B
C
D1
D2
E
通过情况
AC
AC
AC
AC
O
O
难度
800
800
1100
1300
1500
1900
A. Mocha and Math 题面 略
解法 对所有数做与运算后直接输出
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 200010 #define maxm 200010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int inf = 0x7fffffff ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } int main () { int t = read (); while (t--) { int n = read (); int ans = read (); rep (i, 2 , n) { int tmp = read (); ans &= tmp; } printf ("%d\n" , ans); } return 0 ; }
B. Mocha and Red and Blue 题面 略
解法 贪心
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 210 #define maxm 200010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int inf = 0x7fffffff ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } char s[maxn];int main () { int t = read (); while (t--) { int n = read (); scanf ("%s" , s + 1 ); int pre = 0 ; rep (i, 1 , n) { if (s[i] == '?' ) { if (s[pre] != '?' ) pre = i; continue ; } else { if (s[pre] == '?' ) { per (j, i - 1 , pre) s[j] = s[j + 1 ] == 'R' ? 'B' : 'R' ; } } } if (s[pre] == '?' ) rep (i, pre, n) s[i] = s[i - 1 ] == 'R' ? 'B' : 'R' ; puts (s + 1 ); } return 0 ; }
C. Mocha and Hiking 题面 略
解法 简单分析可知,一定可以构造出来可行解,只需要找到以下三种情况之一: ①$n+1\rightarrow 1$ ②$n\rightarrow n+1$ ③$i\rightarrow n+1\rightarrow i+1,i<n$
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 200010 #define maxm 200010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int inf = 0x7fffffff ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } int a[maxn];int main () { int t = read (); while (t--) { int n = read (); rep (i, 1 , n) a[i] = read (); int f = 0 ; a[n + 1 ] = 1 ; rep (i, 1 , n + 1 ) if (a[i] == 1 && a[i - 1 ] == 0 ) f = 1 ; if (f == 0 ) { puts ("-1" ); continue ; } rep (i, 1 , n) { if ((a[i - 1 ] == 0 && a[i] == 1 ) && f) { printf ("%d " , n + 1 ); f = 0 ; } printf ("%d " , i); } if (f) printf ("%d" , n + 1 ); puts ("" ); } return 0 ; }
D1. Mocha and Diana (Easy Version) 题面 向两个图(无环路)中在相同位置加树边,直到不能继续加边
解法 使用并查集判断两点是否在同一个图中,枚举所有边,时间复杂度$O(n^2)$($O\left( n\alpha \left( n \right) \right) $)
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 200010 #define maxm 200010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int inf = 0x7fffffff ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } int par1[maxn], par2[maxn];int ans[maxn][2 ];int find1 (int x) { return x == par1[x] ? x : par1[x] = find1 (par1[x]); } int find2 (int x) { return x == par2[x] ? x : par2[x] = find2 (par2[x]); } int main () { int n = read (), m1 = read (), m2 = read (); rep (i, 1 , n) par1[i] = par2[i] = i; rep (i, 1 , m1) { int u = read (), v = read (); u = find1 (u), v = find1 (v); par1[u] = v; } rep (i, 1 , m2) { int u = read (), v = read (); u = find2 (u), v = find2 (v); par2[u] = v; } int cnt = 0 ; rep (i, 1 , n) { rep (j, i + 1 , n) { int u1 = find1 (i), v1 = find1 (j); int u2 = find2 (i), v2 = find2 (j); if (u1 != v1 && u2 != v2) { par1[u1] = v1; par2[u2] = v2; ans[cnt][0 ] = i, ans[cnt][1 ] = j; cnt++; } } } printf ("%d\n" , cnt); rep (i, 0 , cnt - 1 ) printf ("%d %d\n" , ans[i][0 ], ans[i][1 ]); return 0 ; }
D2. Mocha and Diana (Hard Version) 题面 同D1
解法 可以证明,必然有一个图最后会形成树(有$n-1$条边) 先将1和其他点尝试进行连边,那么图中所有点必属于以下三种集合之一: ①在两个图中均与1相连的点集$S$ ②在图1中与1相连,在图2中不与1相连点集$T_1$ ③在图1中不与1相连,在图2中与1相连点集$T_2$ 分属$T_1$和$T_2$中任意两点之间必然可以加边(否则不满足条件),所以接下来只需将$T_1$和$T_2$的连通块两两配对即可,直至其中一个集合为空(这时某一个图成为树)
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 1000010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int inf = 0x7fffffff ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } int par1[maxn], par2[maxn];int ans[maxn][2 ];int s1[maxn], s2[maxn], p1, p2;int find1 (int x) { return x == par1[x] ? x : par1[x] = find1 (par1[x]); } int find2 (int x) { return x == par2[x] ? x : par2[x] = find2 (par2[x]); } int main () { int n = read (), m1 = read (), m2 = read (); rep (i, 1 , n) par1[i] = par2[i] = i; rep (i, 1 , m1) { int u = read (), v = read (); u = find1 (u), v = find1 (v); par1[u] = v; } rep (i, 1 , m2) { int u = read (), v = read (); u = find2 (u), v = find2 (v); par2[u] = v; } int cnt = 0 ; rep (i, 2 , n) { int u1 = find1 (1 ), v1 = find1 (i); int u2 = find2 (1 ), v2 = find2 (i); if (u1 != v1 && u2 != v2) { par1[u1] = v1; par2[u2] = v2; ans[cnt][0 ] = 1 , ans[cnt][1 ] = i; cnt++; } } rep (i, 2 , n) { int u1 = find1 (1 ), v1 = find1 (i); int u2 = find2 (1 ), v2 = find2 (i); if (u1 != v1) s1[p1++] = i, par1[u1] = v1; else if (u2 != v2) s2[p2++] = i, par2[u2] = v2; } printf ("%d\n" , cnt + min (p1, p2)); rep (i, 0 , cnt - 1 ) printf ("%d %d\n" , ans[i][0 ], ans[i][1 ]); rep (i, 0 , min (p1, p2) - 1 ) printf ("%d %d\n" , s1[i], s2[i]); return 0 ; }
E. Mocha and Stars 题面 给定$n,m$,求满足以下条件的方案数: ①$a_i\in \left[ l_i,r_i \right] ,1\leqslant i\leqslant n$ ②$\sum_{i=1}^n{a_i\leqslant m}$ ③$gcd\left( a_1,…,a_n \right) =1$ 答案对$998244353$取模
解法 设$f(a_1,a_2,…,a_n)$为满足前两个条件的方案数,然后用莫比乌斯反演对第三个条件化简 $\begin{aligned} &\sum_{a_1=l_1}^{r_1}{\sum_{a_2=l_2}^{r_2}{\cdots}}\sum_{a_n=l_n}^{r_n}{[}\mathrm{gcd(}a_1,a_2,…,a_n)=1]f(a_1,a_2,…,a_n)\\ =&\sum_{a_1=l_1}^{r_1}{\sum_{a_2=l_2}^{r_2}{\cdots}}\sum_{a_n=l_n}^{r_n}{f}(a_1,a_2,…,a_n)\sum_{d\mid \mathrm{gcd(}a_1,a_2,…,a_n)}{\mu}(d)\\ =&\sum_{a_1=l_1}^{r_1}{\sum_{a_2=l_2}^{r_2}{\cdots}}\sum_{a_n=l_n}^{r_n}{f}(a_1,a_2,…,a_n)\sum_{d\mid a_1,d\mid a_2,…,d\mid a_n}{\mu}(d)\\ =&\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}{f}(a_1,a_2,…,a_n)\\ \end{aligned}$ 接下来对于每一个d用前缀和+背包计数即可
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define maxn 100010 #define maxm 200010 #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, r, l) for (int i = r; i >= l; i--) #define ll long long using namespace std;const int mod = 998244353 ;int read () { int p = 0 , f = 1 ; char c = getchar (); while (c < 48 || c > 57 ) { if (c == '-' ) f = -1 ; c = getchar (); } while (c >= 48 && c <= 57 ) p = (p << 1 ) + (p << 3 ) + (c ^ 48 ), c = getchar (); return p * f; } int prim[maxn], mu[maxn], cnt;bool vis[maxn];void get_mu (int n) { mu[1 ] = 1 ; rep (i, 2 , n) { if (!vis[i]) { mu[i] = -1 , prim[++cnt] = i; } for (int j = 1 ; j <= cnt && i * prim[j] <= n; j++) { vis[i * prim[j]] = 1 ; if (i % prim[j] == 0 ) break ; mu[i * prim[j]] = -mu[i]; } } } int n, m;int l[maxn], r[maxn];int nl[maxn], nr[maxn];int dp[maxn], f[maxn];int main () { n = read (), m = read (); get_mu (1e5 ); rep (i, 1 , n) l[i] = read (), r[i] = read (); ll ans = 0 ; rep (d, 1 , m) { if (mu[d]) { rep (i, 1 , n) nl[i] = (l[i] + d - 1 ) / d, nr[i] = r[i] / d; int sum = m / d; rep (i, 0 , sum) dp[i] = 1 ; rep (i, 1 , n) { rep (j, 1 , sum) f[j] = 0 ; rep (j, nl[i], sum) { f[j] = dp[j - nl[i]]; if (j - nr[i] - 1 >= 0 ) f[j] = (f[j] - dp[j - nr[i] - 1 ] + mod) % mod; } dp[0 ] = 0 ; rep (j, 1 , sum) dp[j] = (dp[j - 1 ] + f[j]) % mod; } ans = (ans + dp[sum] * mu[d] + mod) % mod; } } printf ("%lld" , ans); return 0 ; }