牛客国庆集训派对Day2 Solution
A 矩阵乘法
思路:
1° 牛客机器太快了,暴力能过。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 5000 5 6 int n, p, m; 7 int a[N][100], b[100][N], ans[N][N]; 8 9 void Run() 10 { 11 while (scanf("%d%d%d", &n, &p, &m) != EOF) 12 { 13 for (int i = 1; i <= n; ++i) 14 for (int j = 1; j <= p; ++j) 15 scanf("%X", &a[i][j]); 16 for (int j = 1; j <= m; ++j) 17 for (int i = 1; i <= p; ++i) 18 scanf("%1d", &b[i][j]); 19 for (int i = 1; i <= n; ++i) 20 for (int j = 1; j <= m; ++j) 21 for (int k = 1; k <= p; ++k) 22 ans[i][j] += a[i][k] * b[k][j]; 23 int res = 0; 24 for (int i = 1; i <= n; ++i) 25 for (int j = 1; j <= m; ++j) 26 res ^= ans[i][j]; 27 printf("%d\n", res); 28 } 29 } 30 31 int main() 32 { 33 #ifdef LOCAL 34 freopen("Test.in", "r", stdin); 35 #endif 36 37 Run(); 38 return 0; 39 }
2°
考虑到p很小,可以将它分块,比如说分成8Bit一块,那么对应的只有256种情况,可以预处理一下,然后乘法变成取值
复杂度大概在(4096 * 4096 * 8) 左右
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 5000 5 6 int n, p, m; 7 int a[N][100], b[100][N], ap[N][10][256], bp[10][N], ans[N][N]; 8 9 void Run() 10 { 11 while (scanf("%d%d%d", &n, &p, &m) != EOF) 12 { 13 for (int i = 0; i < n; ++i) 14 for (int j = 0; j < p; ++j) 15 scanf("%X", &a[i][j]); 16 for (int j = 0; j < m; ++j) 17 for (int i = 0; i < p; ++i) 18 scanf("%1d", &b[i][j]); 19 p = (p - 1) / 8 + 1; 20 for (int i = 0; i < n; ++i) 21 for (int j = 0; j < p; ++j) 22 for (int k = 0; k < 256; ++k) 23 for (int l = 0; l < 8; ++l) 24 if (k & (1 << l)) ap[i][j][k] += a[i][j * 8 + l]; 25 for (int j = 0; j < m; ++j) 26 for (int i = p - 1; i >= 0; --i) 27 for (int k = 7; k >= 0; --k) 28 bp[i][j] = bp[i][j] * 2 + b[k + 8 * i][j]; 29 for (int i = 0; i < n; ++i) 30 for (int j = 0; j < m; ++j) 31 for (int k = 0; k < p; ++k) 32 ans[i][j] += ap[i][k][bp[k][j]]; 33 int res = 0; 34 for (int i = 0; i < n; ++i) 35 for (int j = 0; j < m; ++j) 36 res ^= ans[i][j]; 37 printf("%d\n", res); 38 } 39 } 40 41 int main() 42 { 43 #ifdef LOCAL 44 freopen("Test.in", "r", stdin); 45 #endif 46 47 Run(); 48 return 0; 49 }
B 字符串的幂
留坑。
C 生命游戏
留坑。
D 数格点
留坑。
E 数据排序
留坑。
F 平衡二叉树
思路:显然,答案最大肯定是根节点下左子树是满二叉树,右子树是最小平衡二叉树
深度为n的二叉平衡树的最小节点数 = 左平衡树的最小节点数 + 右平衡树的最小结点树 + 当前树的根节点树(1)
显然 ,右平衡树的高度可以比左平衡树少d
当$n <= d$ 的时候 $F[n] = max(n, 0)$
所以$F[n] = F[n - 1] + F[n - d - 1] +1$
满二叉树的结点个数是$2^n$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 using ll = long long; 4 5 int n, d; 6 ll f[100]; 7 8 ll qpow(ll base, ll n) 9 { 10 ll res = 1; 11 while (n) 12 { 13 if (n & 1) res *= base; 14 base *= base; 15 n >>= 1; 16 } 17 return res; 18 } 19 20 ll work(int h) 21 { 22 if (h <= d) 23 return max(h, 0); 24 if (f[h]) return f[h]; 25 f[h] = work(h - 1) + work(h - d - 1) + 1; 26 return f[h]; 27 } 28 29 void Run() 30 { 31 while (scanf("%d%d", &n, &d) != EOF) 32 { 33 memset(f, 0, sizeof f); 34 printf("%lld\n", qpow(2ll, n - 1) - work(n - d - 1) - 1); 35 } 36 } 37 38 int main() 39 { 40 #ifdef LOCAL 41 freopen("Test.in", "r", stdin); 42 #endif 43 44 Run(); 45 return 0; 46 }
G 数组合并
留坑。
H 卡牌游戏
思路:考虑抽到一张卡片的概率是$\frac{m}{n}$ 那么期望就是 $\frac{n}{m}$ 抽到一张之后再抽一张的概率是$\frac{m - 1}{n - 1}$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int t, n, m, k; 5 6 int main() 7 { 8 scanf("%d", &t); 9 for (int kase = 1; kase <= t; ++kase) 10 { 11 scanf("%d%d%d", &n, &m, &k); 12 double res = 0; 13 for (int i = 1; i <= k; ++i, --n, --m) 14 res += n * 1.0 / m; 15 printf("Case #%d: %.10f\n", kase, res); 16 } 17 return 0; 18 }
I 游戏
留坑。
J 魔法阵
留坑。
K 排队
留坑。