从这里开始
- 题目列表
- Problem A New Year and Counting Cards
- Problem B New Year and Buggy Bot
- Problem C New Year and Curling
- Problem D New Year and Arbitrary Arrangement
- Problem E New Year and Entity Enumeration
- Problem F New Year and Rainbow Roads
- Problem G New Year and Original Order
- Problem H New Year and Boolean Bridges
Problem A New Year and Counting Cards
题目大意
(略)
题目怎么说就怎么做。
Code
1 /** 2 * Codeforces 3 * Problem#908A 4 * Accepted 5 * Time: 31ms 6 * Memory: 0k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int ans = 0; 13 char str[55]; 14 boolean need[256]; 15 16 inline void init() { 17 scanf("%s", str); 18 } 19 20 inline void solve() { 21 need[‘a‘] = need[‘e‘] = need[‘i‘] = need[‘u‘] = need[‘o‘] = true; 22 need[‘1‘] = need[‘3‘] = need[‘5‘] = need[‘7‘] = need[‘9‘] = true; 23 for (char *p = str; *p; p++) 24 ans += need[*p]; 25 printf("%d\n", ans); 26 } 27 28 int main() { 29 init(); 30 solve(); 31 return 0; 32 }
Problem A
Problem B New Year and Buggy Bot
题目大意
(略)
暴力枚举映射,然后模拟check。
Code
1 /** 2 * Codeforces 3 * Problem#908B 4 * Accepted 5 * Time: 31ms 6 * Memory: 0k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int mov[2][4] = {{0, 1, 0, -1}, {1, 0, -1, 0}}; 13 14 int n, m, l; 15 int sx, sy; 16 int key[4]; 17 char str[105]; 18 char a[55][55]; 19 20 inline void init() { 21 scanf("%d%d", &n, &m); 22 for (int i = 1; i <= n; i++) { 23 scanf("%s", a[i] + 1); 24 for (int j = 1; j <= m; j++) { 25 if (a[i][j] == ‘S‘) { 26 sx = i, sy = j; 27 } 28 } 29 } 30 scanf("%s", str); 31 l = strlen(str); 32 for (int i = 0; i < l; i++) 33 str[i] -= ‘0‘; 34 } 35 36 int ans = 0; 37 boolean simulate() { 38 int curx = sx, cury = sy; 39 for (int i = 0; i < l; i++) { 40 int dir = key[str[i]]; 41 curx += mov[0][dir], cury += mov[1][dir]; 42 if (curx < 1 || curx > n || cury < 1 || cury > m) 43 return false; 44 if (a[curx][cury] == ‘#‘) 45 return false; 46 if (a[curx][cury] == ‘E‘) 47 return true; 48 } 49 return false; 50 } 51 52 inline void solve() { 53 for (int a = 0; a < 4; a++) 54 for (int b = 0; b < 4; b++) 55 if (a != b) 56 for (int c = 0; c < 4; c++) 57 if (c != b && c != a) 58 for (int d = 0; d < 4; d++) 59 if ((a != d) && (b != d) && (c != d)) { 60 key[0] = a, key[1] = b; 61 key[2] = c, key[3] = d; 62 ans += simulate(); 63 } 64 printf("%d\n", ans); 65 } 66 67 int main() { 68 init(); 69 solve(); 70 return 0; 71 }
Problem B
Problem C New Year and Curling
题目大意
有$n$个半径为$r$的圆,依次将第$i$个圆的圆心放在$x = x_i$的无限远处,然后向$y = 0$滑动(横坐标不变),当圆接触到$y = 0$或者接触到之前的圆就会停止滑动。求出每个圆停止时圆心的纵坐标。
暴力找到每个圆与哪个圆最终接触,或者接触到$y = 0$。然后用小学几何知识计算圆心的纵坐标。
Code
1 /** 2 * Codeforces 3 * Problem#908C 4 * Accepted 5 * Time: 31ms 6 * Memory: 0k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n, r; 13 int *x; 14 double *y; 15 16 inline void init() { 17 scanf("%d%d", &n, &r); 18 x = new int[(n + 1)]; 19 y = new double[(n + 1)]; 20 for (int i = 1; i <= n; i++) 21 scanf("%d", x + i); 22 } 23 24 inline void solve() { 25 int d = r << 1; 26 for (int i = 1; i <= n; i++) { 27 y[i] = r; 28 for (int j = 1; j < i; j++) { 29 if (abs(x[i] - x[j]) <= d) { 30 y[i] = max(y[i], y[j] + sqrt(d * d - (x[i] - x[j]) * (x[i] - x[j]))); 31 } 32 } 33 printf("%.9lf ", y[i]); 34 } 35 } 36 37 int main() { 38 init(); 39 solve(); 40 return 0; 41 }
Problem C
Problem D New Year and Arbitrary Arrangement
题目大意
有一个空字符串$s$,每次有$p_a$的概率向$s$的末尾添加一个字符a,有$p_b$的概率向$s$的末尾添加一个字符b。当$s$中子序列ab的个数不少于$k$的时候停止。问停止时子序列ab的个数的期望。
暴力dp是用$f_{i, j}$表示当前有$i$个子序列,其中已经有$j$个a的概率。
但是$j$可以无限大。不难注意到当$a \geqslant k$的时候只要出现b就会停止,这一部分可以用等比数列求和的方法直接计算。
因此我们只用暴力处理$j < k$的情况。
Code
1 /** 2 * Codeforces 3 * Problem#908D 4 * Accepted 5 * Time: 31ms 6 * Memory: 4100k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int Mod = 1e9 + 7; 13 14 int add(int a, int b) { 15 return ((a += b) >= Mod) ? (a - Mod) : (a); 16 } 17 18 int mul(int a, int b) { 19 return a * 1ll * b % Mod; 20 } 21 22 void exgcd(int a, int b, int& x, int& y) { 23 if (!b) 24 x = 1, y = 0; 25 else { 26 exgcd(b, a % b, y, x); 27 y -= (a / b) * x; 28 } 29 } 30 31 int inv(int a) { 32 int x, y; 33 exgcd(a, Mod, x, y); 34 return (x < 0) ? (x + Mod) : (x); 35 } 36 37 const int N = 1024; 38 39 int k, pa, pb; 40 int f[N][N]; 41 42 inline void init() { 43 scanf("%d%d%d", &k, &pa, &pb); 44 pa = mul(pa, inv(pa + pb)); 45 pb = Mod + 1 - pa; 46 } 47 48 int ans = 0; 49 inline void solve() { 50 f[0][1] = 1; 51 int _pb = inv(pb); 52 for (int s = 0; s < k; s++) { 53 for (int cnt_a = 1; cnt_a < k; cnt_a++) { 54 int val = f[s][cnt_a]; 55 if (s + cnt_a < k) { 56 f[s + cnt_a][cnt_a] = add(f[s + cnt_a][cnt_a], mul(val, pb)); 57 } else { 58 ans = add(ans, mul(mul(val, pb), s + cnt_a)); 59 } 60 f[s][cnt_a + 1] = add(f[s][cnt_a + 1], mul(val, pa)); 61 } 62 int prob = mul(f[s][k], pb); 63 int E = mul(k + s, _pb); 64 E = add(E, mul(pa, mul(_pb, _pb))); 65 ans = add(ans, mul(E, prob)); 66 } 67 printf("%d\n", ans); 68 } 69 70 int main() { 71 init(); 72 solve(); 73 return 0; 74 }
Problem D
Problem E New Year and Entity Enumeration
题目大意
(原题题面很简洁)
设$f_i$表示集合$S$中第$i$位为1的数的and和。
对于$j \neq i$,那么要么$f_i = f_j$要么$f_i \cap f_j = \varnothing$。
设$b = f_i \cap f_j$,若$f_i \neq f_j$,并且$b\neq \varnothing$
- 如果$i,j \in b$,容易推出$f_i = f_j$,矛盾。
- 如果$i\in b, j\not \in b$,那么必然存在一个数第$i$位为1,第$j$为0,使得$j\not \in f_i$,考虑这个数的补,由此推出$i \not \in b$,矛盾。
- 如果$i\not \in b, j \in b$,同理可得。
- 如果$i\not \in b, j \not \in b$,那么存在一个$x\in b$,所以存在一个数$y$使得在第$i$位为1,第$x$位为1, 第$j$位为0,取它的补,那么就有第$i$位为0,第$x$位为0,第$j$位为1,由此推出$x\not \in b$,矛盾。
那么$f_i$可以看成对这几个二进制位的一个划分,对于$j\in f_i$的二进制位划在同一个集合。
可以证明任意一个划分和一个集合是一一对应的。
也可以证明对于$S$的划分一定是由$T$的一个划分中每个集合继续划分得到的。
然而我都不会证明。
这个集合划分的方案数Bell数,每一部分独立,乘起来就好了。设$s_i$表示第$i$个划分出来的集合的大小,那么
$ans = \prod_{i = 1} B_{s_i}$
Code
1 /** 2 * Codeforces 3 * Problem#908E 4 * Accepted 5 * Time: 31ms 6 * Memory: 4000k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int Mod = 1e9 + 7; 13 const int N = 1005, M = 55; 14 15 int add(int a, int b) { 16 return ((a += b) >= Mod) ? (a - Mod) : (a); 17 } 18 19 int mul(int a, int b) { 20 return a * 1ll * b % Mod; 21 } 22 23 int n, m; 24 char str[N]; 25 boolean vis[N]; 26 bitset<N> a[M], f, all; 27 28 inline void init() { 29 scanf("%d%d", &n, &m); 30 for (int i = 0; i < m; i++) { 31 scanf("%s", str); 32 for (int j = 0; j < n; j++) 33 if (str[j] == ‘1‘) 34 a[i].set(j); 35 } 36 } 37 38 int bell[N]; 39 int comb[N][N]; 40 41 inline void solve() { 42 comb[0][0] = 1; 43 for (int i = 1; i <= n; i++) { 44 comb[i][0] = comb[i][i] = 1; 45 for (int j = 1; j < i; j++) { 46 comb[i][j] = add(comb[i - 1][j - 1], comb[i - 1][j]); 47 } 48 } 49 bell[0] = 1; 50 for (int i = 1; i <= n; i++) { 51 for (int j = 1; j <= i; j++) { 52 bell[i] = add(bell[i], mul(comb[i - 1][j - 1], bell[i - j])); 53 } 54 } 55 for (int i = 0; i < n; i++) 56 all.set(i); 57 int ans = 1; 58 for (int i = 0; i < n; i++) { 59 if (vis[i]) 60 continue; 61 f.set(); 62 for (int j = 0; j < m; j++) { 63 if (a[j].test(i)) { 64 f &= a[j]; 65 } else { 66 f &= (a[j] ^ all); 67 } 68 } 69 int cnt = 0; 70 for (int j = 0; j < n; j++) { 71 if (f.test(j)) { 72 vis[j] = true; 73 cnt++; 74 } 75 } 76 ans = mul(ans, bell[cnt]); 77 } 78 printf("%d\n", ans); 79 } 80 81 int main() { 82 init(); 83 solve(); 84 return 0; 85 }
Problem E
Problem F New Year and Rainbow Roads
题目大意
有三种颜色的点在数轴上,每个点的颜色是红蓝绿中的一种,连接两个点的代价是两个点的距离。要求删掉所有红点或者蓝点后剩下的点依然连通。问最小代价。
开始读错题,以为还要满足是棵树。
有一些比较显然的结论:
- 红点和蓝点间连边不优,因为它根本不会有用。
- 绿点和绿点间,绿点和边界的部分是独立的。(连边跨过绿点可以变成连向绿点)。
然后红点和蓝点独立了。
绿点和边界之间连法只有一种。比较显然。
考虑绿点和绿点间的连边。
- 绿点和绿点有边,那么断开中间两个相邻的距离最远的同色点之间的边。
- 绿点和绿点之间没有边,那么只能每种颜色连成一条链。
如果没有绿点,那么红点和蓝点各自连成一条链。
Code
1 /** 2 * Codeforces 3 * Problem#908F 4 * Accepted 5 * Time: 93ms 6 * Memory: 2400k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 #define ll long long 13 14 const signed inf = (signed) (~0u >> 1); 15 const int N = 3e5 + 5; 16 17 int n; 18 char str[5]; 19 int p[N], col[N]; 20 21 inline void init() { 22 scanf("%d", &n); 23 for (int i = 1; i <= n; i++) { 24 scanf("%d%s", p + i, str); 25 if (str[0] == ‘R‘) 26 col[i] = 1; 27 if (str[0] == ‘G‘) 28 col[i] = 2; 29 } 30 } 31 32 ll ans = 0; 33 void calc(int l, int r) { 34 ll delta = (p[r] - p[l]) * 3ll; 35 int reduce1 = 0, reduce2 = 0; 36 int lst_b = p[l], lst_r = p[l]; 37 for (int i = l + 1; i < r; i++) { 38 if (col[i] == 1) { 39 reduce2 = max(reduce2, p[i] - lst_r); 40 lst_r = p[i]; 41 } else { 42 reduce1 = max(reduce1, p[i] - lst_b); 43 lst_b = p[i]; 44 } 45 } 46 reduce1 = max(reduce1, p[r] - lst_b); 47 reduce2 = max(reduce2, p[r] - lst_r); 48 delta -= reduce1; 49 delta -= reduce2; 50 delta = min(delta, (ll) (p[r] - p[l]) << 1); 51 ans += delta; 52 } 53 54 inline void solve() { 55 int lst = -1; 56 int first_b = -1, first_r = -1; 57 for (int i = 1; i <= n; i++) { 58 if (col[i] == 2) { 59 if (lst == -1) { 60 if (first_b != -1) 61 ans += p[i] - first_b; 62 if (first_r != -1) 63 ans += p[i] - first_r; 64 } else { 65 calc(lst, i); 66 } 67 lst = i; 68 } else { 69 if (first_b == -1 && !col[i]) 70 first_b = p[i]; 71 if (first_r == -1 && col[i] == 1) 72 first_r = p[i]; 73 } 74 } 75 if (lst == -1) { 76 int bmi = inf, bmx = -1; 77 int rmi = inf, rmx = -1; 78 for (int i = 1; i <= n; i++) { 79 if (col[i] == 0) { 80 bmi = min(bmi, p[i]); 81 bmx = max(bmx, p[i]); 82 } else { 83 rmi = min(rmi, p[i]); 84 rmx = max(rmx, p[i]); 85 } 86 } 87 if (~bmx) 88 ans += bmx - bmi; 89 if (~rmx) 90 ans += rmx - rmi; 91 } else { 92 int last_b = -1, last_r = -1; 93 for (int i = n; i; i--) { 94 if (col[i] == 2) { 95 if (~last_b) 96 ans += last_b - p[i]; 97 if (~last_r) 98 ans += last_r - p[i]; 99 break; 100 } 101 102 if (last_b == -1 && col[i] == 0) 103 last_b = p[i]; 104 if (last_r == -1 && col[i] == 1) 105 last_r = p[i]; 106 } 107 } 108 printf("%I64d", ans); 109 } 110 111 int main() { 112 init(); 113 solve(); 114 return 0; 115 }
Problem F
Problem G New Year and Original Order
题目大意
定义一个数的权值是将它的所有数字从大到小排序后得到的数,给定$X$,问不超过$X$的所有正整数的权值和。
枚举位数,计算排序后,这一位大于等于$k$的数的个数。
Code
1 /** 2 * Codeforces 3 * Problem#908G 4 * Accepted 5 * Time: 499ms 6 * Memory: 39000k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 const int N = 705, Mod = 1e9 + 7; 13 14 int add(int a, int b) { 15 return ((a += b) >= Mod) ? (a - Mod) : (a); 16 } 17 18 int mul(int a, int b) { 19 return a * 1ll * b % Mod; 20 } 21 22 int n; 23 int ans = 0; 24 char str[N]; 25 int f[N][10][N][2]; 26 27 inline void init() { 28 scanf("%s", str + 1); 29 n = strlen(str + 1); 30 for (int i = 1; i <= n; i++) 31 str[i] -= ‘0‘; 32 } 33 34 int dp(int bit, int num, int rest, boolean onlim) { 35 if (!bit) 36 return !rest; 37 int& rt = f[bit][num][rest][onlim]; 38 if (~rt) 39 return rt; 40 rt = 0; 41 int lim = (onlim) ? (str[n - bit + 1]) : (9); 42 for (int i = 0; i <= lim; i++) { 43 boolean nx_onlim = onlim && i == lim; 44 rt = add(rt, dp(bit - 1, num, max(rest - (i >= num), 0), nx_onlim)); 45 } 46 return rt; 47 } 48 49 inline void solve() { 50 memset(f, -1, sizeof(f)); 51 int base = 1; 52 for (int bit = 1; bit <= n; bit++, base = mul(base, 10)) { 53 for (int num = 1, tmp = base; num < 10; num++, tmp = add(tmp, base)) { 54 // int g = dp(n, num, bit, true, false); 55 ans = add(ans, mul(dp(n, num, bit, true), base)); 56 // cerr << bit << " " << num << " " << g << ‘\n‘; 57 } 58 } 59 printf("%d\n", ans); 60 } 61 62 int main() { 63 init(); 64 solve(); 65 return 0; 66 }
Problem G
Problem H New Year and Boolean Bridges
题目大意
有一个有向图,对于两个点$(u, v)$,已知任意两点满足下列条件之一
- $u$能到达$v$并且$v$能到达$u$
- $u$能到达$v$或者$v$能到达$u$
- $u$能到达$v$和$v$能到达$u$恰好有一条成立
问满足条件的图中,最少的边数。无解输出-1.
$and$关系是强连通,可以先用并查集把这一部分先缩起来。如果有两个在同一强连通分量内的点有$XOR$的关系,那么答案是$-1$。
对于如果一个强连通分量的大小为$x$,那么当$x = 1$时,它内部的最少边数为$0$,否则为$1$。
对于剩下的点,一种合法的方案是是排成一行,连成一条链。
这个还可以怎么优化?考虑对于一些或关系,我把它变成强连通关系,如果这两个强连通分量的大小都大于1,那么我可以省去一条边。
因此大小为1的强连通分量对答案的贡献总是$1$(当只存在大小为1的强连通分量时需要特判)。
考虑剩下的连通块,它至多有$23$个。
首先可以预处理出$f_{0,s}$,表示$s$中的点能不能连成一个强连通分量。
于是我们可以得到一个优秀的$O(3^{\left \lfloor \frac{n}{2} \right \rfloor})$的暴力dp。
设$f_{k, s}$表示添加$k$条边,能否是$s$中的点连成一条链。
对于$f_{k, s}$能否为真,等价于判断$\sum_{a\cup b = s} f_{k - 1, a}f_{0, b}$是否非0.
因此$f_{k}$由$f_{k - 1}$和$f_0$做或卷积得到。
每次卷积完用容斥解一下$f_{k, U}$。
Code
1 /** 2 * Codeforces 3 * Problem#908H 4 * Accepted 5 * Time: 592ms 6 * Memory: 98500k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 template <typename T> 13 void pcopy(T* pst, const T* ped, T* pval) { 14 for ( ; pst != ped; *(pst++) = *(pval++)); 15 } 16 17 const int N = 50, S = 1 << 23; 18 19 int n; 20 int ans; 21 int a[N][N]; // 0 : or, 1 : xor, 2 : and, 3 : nothing 22 int G[24][24]; // after union 23 int uf[N], sz[N]; 24 int f[S], g[S], bit[S]; 25 26 int find(int x) { 27 return (uf[x] == x) ? (x) : (uf[x] = find(uf[x])); 28 } 29 30 boolean have_and = false; 31 inline void init() { 32 static char str[N + 4]; 33 scanf("%d", &n); 34 for (int i = 0; i < n; i++) 35 uf[i] = i; 36 for (int i = 0; i < n; i++) { 37 scanf("%s", str); 38 for (int j = 0; j < n; j++) { 39 if (i == j) { 40 a[i][j] = 3; 41 } else if (str[j] == ‘A‘) { 42 a[i][j] = 2; 43 have_and = true; 44 uf[find(i)] = find(j); 45 } else if (str[j] == ‘X‘) { 46 a[i][j] = 1; 47 } 48 } 49 } 50 } 51 52 void fwt(int* f, int n) { 53 for (int i = 1; i < n; i <<= 1) { 54 for (int j = 0; j < n; j++) { 55 if (j & i) { 56 f[j] += f[j ^ i]; 57 } 58 } 59 } 60 } 61 62 inline int discrete() { 63 static int id[N]; 64 vector<int> val; 65 for (int i = 0; i < n; i++) { 66 sz[find(i)]++; 67 } 68 for (int i = 0; i < n; i++) { 69 if (find(i) == i && sz[i] > 1) { 70 val.push_back(i); 71 } 72 } 73 for (int i = 0; i < n; i++) { 74 id[i] = lower_bound(val.begin(), val.end(), find(i)) - val.begin(); 75 } 76 for (int i = 0; i < n; i++) { 77 for (int j = 0; j < n; j++) { 78 int u = find(i), v = find(j); 79 if (sz[u] == 1 || sz[v] == 1) { 80 continue; 81 } 82 if (u ^ v) { 83 G[id[u]][id[v]] |= a[i][j]; 84 } 85 } 86 } 87 return val.size(); 88 } 89 90 #define sgn(__) (((__) & 1) ? (-1) : (1)) 91 92 inline void solve() { 93 if (!have_and) { 94 printf("%d\n", n - 1); 95 return; 96 } 97 for (int i = 0; i < n; i++) { 98 for (int j = i + 1; j < n; j++) { 99 if (a[i][j] == 1 && find(i) == find(j)) { 100 puts("-1"); 101 return; 102 } 103 } 104 } 105 ans = n; 106 n = discrete(); 107 f[0] = 1; 108 for (int i = 1; i < (1 << n); i++) { 109 int x = -1; 110 for (int j = 0; j < n; j++) { 111 if (i & (1 << j)) { 112 x = j; 113 break; 114 } 115 } 116 assert(~x); 117 if (!f[i ^ (1 << x)]) 118 continue; 119 f[i] = 1; 120 for (int j = x + 1; j < n && f[i]; j++) { 121 if (i & (1 << j)) { 122 f[i] &= !G[x][j]; 123 } 124 } 125 } 126 pcopy(g, g + (1 << n), f); 127 int all = (1 << n) - 1; 128 bit[0] = 0; 129 for (int s = 1; s <= all; s++) { 130 bit[s] = bit[s >> 1] + (s & 1); 131 } 132 fwt(f, all + 1); 133 fwt(g, all + 1); 134 for (int cnt = 0; cnt <= n; cnt++, ans++) { 135 int fn = 0; 136 for (int j = 0; j <= all; j++) { 137 fn += g[j] * sgn(n - bit[j]); 138 } 139 if (fn) { 140 break; 141 } 142 for (int j = 0; j <= all; j++) 143 g[j] = g[j] * f[j]; 144 } 145 printf("%d\n", ans); 146 } 147 148 int main() { 149 init(); 150 solve(); 151 return 0; 152 }
Problem H
原文地址:https://www.cnblogs.com/yyf0309/p/10222956.html