1. 程式人生 > >牛客練習賽1 補題記錄

牛客練習賽1 補題記錄

樹形 中文 push .html 分答 subst clas n) str

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 }
View Code

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 }
View Code

C 圈圈

思路 Fighting Heart

循環移位的同時序列每個數都模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 }
View Code

牛客練習賽1 補題記錄