A 矩阵
中文题意,要找一个最大的k阶子矩阵在原矩阵中出现过两次。
需要将这个矩阵进行Hash,也就是需要二维Hash,先把每一行Hash了,再把每一列Hash了,有一点前缀的感觉。
预处理完Hash值之后,二分答案k,check过程是在$O(n ^ 2)$枚举起点,这里其实枚举终点方便一些,边界比较好处理,把每个k阶矩阵的hash值存下来,最后看有没有两个一样的。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 567; 5 6 typedef unsigned long long ull; 7 int n, m; 8 char mat[N][N]; 9 ull hs[N * N], hs1[N][N], hs2[N][N]; 10 ull pw1[N], pw2[N]; 11 ull seed1 = 17, seed2 = 9191891; 12 13 bool check(int k) { 14 int tot = 0; 15 for (int i = k; i <= n; ++i) { 16 for (int j = k; j <= m; ++j) { 17 ull temp = hs2[i][j] - hs2[i][j - k] * pw1[k]; 18 temp -= hs2[i - k][j] * pw2[k]; 19 temp += hs2[i - k][j - k] * pw1[k] * pw2[k]; 20 hs[++tot] = temp; 21 } 22 } 23 24 sort(hs + 1, hs + 1 + tot); 25 for (int i = 1; i <= tot; ++i) { 26 if (hs[i] == hs[i - 1]) return 1; 27 } 28 return false; 29 } 30 31 int main() { 32 pw1[0] = pw2[0] = 1; 33 for (int i = 1; i < N; ++i) { 34 pw1[i] = pw1[i - 1] * seed1; 35 pw2[i] = pw2[i - 1] * seed2; 36 } 37 38 scanf("%d%d", &n, &m); 39 for (int i = 1; i <= n; ++i) scanf("%s", mat[i] + 1); 40 41 for (int i = 1; i <= n; ++i) { 42 for (int j = 1; j <= m; ++j) { 43 hs1[i][j] = hs1[i][j - 1] * seed1 + (mat[i][j] - ‘a‘); 44 } 45 } 46 for (int i = 1; i <= n; ++i) { 47 for (int j = 1; j <= m; ++j) { 48 hs2[i][j] = hs2[i - 1][j] * seed2 + hs1[i][j]; 49 } 50 } 51 52 int mid, ans = 0, lb = 1, ub = min(n, m); 53 while (lb <= ub) { 54 mid = (lb + ub) / 2; 55 if (check(mid)) { 56 ans = mid; lb = mid + 1; 57 } else { 58 ub = mid - 1; 59 } 60 } 61 printf("%d\n", ans); 62 return 0; 63 }
B 树
每条链上的颜色要一样,题目其实和树形无关。
状态表示:dp(i, j)表示前i个结点染了j个颜色的方案数,那么新加一个点进去,要么和前面的结点是同一个颜色,要么就是新的颜色。
转移方程:dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * (k - j + 1));
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 345; 5 const int mod = 1e9 + 7; 6 int n, k; 7 vector<vector<int>> T; 8 long long dp[N][N]; 9 int main() { 10 scanf("%d%d", &n, &k); 11 T.resize(n); 12 for (int i = 1; i < n; ++i) { 13 int x, y; 14 scanf("%d%d", &x, &y); 15 } 16 dp[0][0] = 1; 17 for (int i = 1; i <= n; ++i) { 18 for (int j = 1; j <= k; ++j) { 19 dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * (k - j + 1)) % mod; 20 } 21 } 22 long long ans = 0; 23 for (int i = 1; i <= k; ++i) (ans += dp[n][i]) %= mod; 24 printf("%lld\n", ans); 25 return 0; 26 }
C 圈圈
循环移位的同时序列每个数都模m的++,要求循环移位后,字典序最小的那个序列的第k项。
这种循环移位的,一般可以先把序列复制一遍,变成2n的长度,比较方便。
可以发现,每个元素如果有那么仅有一次变为0的机会,当有数字变为0了,需要重新判断。
枚举每一轮会变为0的位置,字典序最小肯定是从这些位置中产生的。
然后又是Hash,二分找两个串的lcp,判断后一位的大小来比较两个串。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef unsigned long long ull; 5 const int N = 50000 * 2 + 5; 6 int n, m, k; 7 int a[N]; 8 int ans[N]; 9 vector<int> pos[N]; 10 ull sd, hs[N], sdp[N]; 11 12 bool check(int x, int y, int L) { 13 if (L == 0) return 1; 14 ull u = hs[x] - hs[x + L] * sdp[L]; 15 ull v = hs[y] - hs[y + L] * sdp[L]; 16 return u == v; 17 } 18 // ok(x, y, l) : substr(x, l) > substr(y, l) ? 19 bool ok(int x, int y, int t) { 20 int lb = 0, ub = n, ret = -1; 21 while (lb <= ub) { 22 int mid = (lb + ub) / 2; 23 if (check(x, y, mid)) { 24 ret = mid; lb = mid + 1; 25 } else { 26 ub = mid - 1; 27 } 28 } 29 if (ret == n) return 0; 30 return ((a[x + ret] + t) % m) < ((a[y + ret] + t) % m); 31 } 32 33 int main() { 34 scanf("%d%d%d", &n, &m, &k); 35 36 for (int i = 0; i < n; ++i) { 37 scanf("%d", a + i); 38 a[n + i] = a[i]; 39 pos[(m - a[i]) % m].push_back(i); 40 } 41 42 sd = 19260817; sdp[0] = 1; 43 for (int i = 1; i < N; ++i) { 44 sdp[i] = sdp[i - 1] * sd; 45 } 46 hs[2 * n] = 0; 47 for (int i = 2 * n - 1; ~i; --i) { 48 hs[i] = hs[i + 1] * sd + a[i]; 49 } 50 51 int x = 0; 52 for (int i = 0; i < n; ++i) { 53 if (ok(i, x, 0)) x = i; 54 } 55 ans[0] = a[x + k - 1]; 56 for (int i = 1; i < m; ++i) { 57 if (pos[i].empty()) { 58 ans[i] = ans[i - 1] + 1; 59 continue; 60 } 61 x = pos[i][0]; 62 for (int j = 1; j < (int)pos[i].size(); ++j) { 63 if (ok(pos[i][j], x, i)) x = pos[i][j]; 64 } 65 ans[i] = (a[x + k - 1] + i) % m; 66 } 67 68 for (int i = 0; i < m; ++i) { 69 printf("%d\n", ans[i]); 70 } 71 }