码迷,mamicode.com
首页 > 其他好文 > 详细

牛客练习赛1 补题记录

时间:2018-02-07 22:58:17      阅读:320      评论:0      收藏:0      [点我收藏+]

标签:ack   pre   枚举   mod   play   产生   数字   empty   状态   

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 补题记录

标签:ack   pre   枚举   mod   play   产生   数字   empty   状态   

原文地址:https://www.cnblogs.com/wfgu/p/8428351.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!