牛客练习赛1 补题记录

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 圈圈

思路 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 }

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

时间: 2024-08-29 15:08:15

牛客练习赛1 补题记录的相关文章

牛客练习赛11 B trie树+拓扑判环 E 分治求平面最近点对

牛客练习赛11 B  假的字符串题意:给定n个字符串,互不相等,你可以任意指定字符之间的大小关系(即重定义字典序),求有多少个串可能成为字典序最小的串,并输出它们. tags:好题 对于一个字符串, 1]如有其它字符串是它的前缀,那肯定不可能.这个直接用字典树处理就可以. 2]但如果以这个字符串为最小,怎么判定其它字符串不会矛盾呢? 其实矛盾的情况详细一点说是: 比如要以  abcd 为最小, 但又有另一个字符串 aba ,这就矛盾了. 对这种情况,在跑字典树的时候,我们对有相同父亲结点的多个儿

牛客网在线判题系统JavaScript(V8)使用

JavaScript作为一种弱类型的编程语言,语法和C/C++.JAVA等存在差别,但是对于大部算法题,不只是C/C++.JAVA,也依然可以使用JavaScript来实现.所以在牛客网中,如果你喜欢JavaScript这门编程语言,同时对数据结构与算法感兴趣,当然可以使用这门语言去刷编程题. 大家有没有跟我碰到过和我类似的情况,在牛客网刷编程题的时候,编译器提供的是Javascript(v8 6.0.0),在线编程部分前端编程还好,只用在题目给出的函数中补充就行.但苦恼的是,我们不知道如何像C

转载:牛客练习赛17 c 规律题

转载:https://www.cnblogs.com/zzqc/p/8995135.html C.链接:https://www.nowcoder.com/acm/contest/109/C来源:牛客网 题目描述 给定长度为n的数组a,定义一次操作为:1. 算出长度为n的数组s,使得si= (a[1] + a[2] + ... + a[i]) mod 1,000,000,007:2. 执行a = s:现在问k次操作以后a长什么样. 输入描述: 第一行两个整数n,k(1 <= n <= 2000,

牛客练习赛18

链接:https://www.nowcoder.com/acm/contest/110/A来源:牛客网 最大乘积 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 这题要你回答T个询问,给你一个正整数S,若有若干个正整数的和为S,则这若干的数的乘积最大是多少?请输出答案除以2000000000000000003(共有17 个零) 的余数. 举例来说,当 S = 5 时,若干个数的和为 5

牛客练习赛16

A   字典序最大的子序列 > 25960019 一开始潜意识看成了循环,最长子串-- 找每个字母最晚出现的位置,那么比它小的字符必须出现在 在它之后的位置 1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <set> 6 #include <map> 7 #include <list>

牛客练习赛17 B-好位置

传送门 题意:本来惯例中文题不解释的, 但是有些人不懂这个题意, 简单的来说, 就是s1每一个的每一个字符都可以和别的字符构成一个子串 == s2.  算了还是惯例中文题意不解释吧. 题解:其实以前写nowcoder的比赛的时候,已经被nowcoder的数据给震惊过了,可是昨天发现有人交随机数过的,有人交Java套数据过的,汗,然后中午被某人催了写一下这个题目. 其实很简单,对于s1, 我们从开头匹配出最先的s2子串,并且对应的位置记录下L[i], 在从末尾开始匹配出最后的s2子串,并且记录下对

牛客华为在线编程题----简单密码破解

首先题目如下: /** * * 假设渊子原来一个BBS上的密码为zvbo9441987,为了方便记忆,他通过一种算法把这个密码变换成YUANzhi1987, * 这个密码是他的名字和出生年份,怎么忘都忘不了,而且可以明目张胆地放在显眼的地方而不被别人知道真正的密码. * * 他是这么变换的,大家都知道手机上的字母: 1--1, abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, * tuv--8 wxyz--9, 0--0,就这么简单,渊子把密码中出

牛客练习赛6

A题: 方法一:一元二次方程求解,但是会有精度误差,+1个点特判一下. #include <bits/stdc++.h> using namespace std; typedef unsigned long long ll; ll t; ll x,y,z; int main() { //freopen("in.txt","r",stdin); ll n; scanf("%lld%lld",&n,&t); ll ans

牛客练习赛7

退役不退坑,以后还是要每周打一场比赛,训练思维了. 看来是很久没敲竞赛代码了,很生疏了.其余几题明天再填~~~ 在Alice和Bob?前的是两个骰?,上?分别写了六个数字. Alice和Bob轮流丢掷骰?,Alice选择第?个骰?,?Bob选择第?个,如果 谁投掷出的数更?,谁就可以获胜. 现在给定这两个骰?上的6个数字,你需要回答是Alice获胜?率更?,还是 Bob获胜?率更?.(请注意获胜?率相同的情况) 输入描述: 第???个数T,表?数据个数.接下来的每?组数据?共有2?,每??有6个