2020牛客暑期多校训练营(第二场)
概要
| A | B | C | D | E | F | G | H | I | J | K | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| ldx | √ | 🎈 | 🎈 | √ | √ | ||||||
| wyc | 🎈 | ||||||||||
| yjs |
题目
A-All with Pairs
题意
给n个字符串,f(s,t)表示s的前缀和t的后缀的最长公共子串,计算\(\sum_{i=1}^{n}\sum_{j=1}^{n}f(s_{i},s_{j})^{2}(mod \quad 998244353)\)的结果。
思路
hash求出每个字符串后缀的hash值,保存下来,判断每个字符串前缀的hash值存了多少。
因为题目所求的是最长公共子串,所以有时会有计算重复的,例如aba会同时计算a和aba,因此对每个字符串计算next[],找到更长的公共子串后将next[]中的答案删除。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MOD = 998244353;
const int MAXN = 1000005;
const int SEED = 131;
unordered_map<ull, int> hashmp;
ull base[MAXN], hs[MAXN];
ll sum[MAXN], nex[MAXN];
string str[100005];
void buildnext(string s)
{
nex[0] = -1;
int i = 0, j = -1, plen = s.length();
while (i < plen)
{
if (j == -1 || s[i] == s[j])
nex[++i] = ++j;
else
j = nex[j];
}
}
ull gethash(int l, int r)
{
return hs[r] - hs[l - 1] * base[r - l + 1];
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
base[0] = 1;
for (int i = 1; i < MAXN; ++i)
base[i] = base[i - 1] * SEED;
for (int i = 1; i <= n; ++i)
{
cin >> str[i];
int len = str[i].length();
for (int j = 1; j <= len; ++j)
hs[j] = hs[j - 1] * SEED + str[i][j - 1];
for (int j = len; j >= 1; --j)
hashmp[gethash(j, len)]++;
}
ll ans = 0;
for (int i = 1; i <= n; ++i)
{
buildnext(str[i]);
int len = str[i].length();
for (int j = 1; j <= len; ++j)
hs[j] = hs[j - 1] * SEED + str[i][j - 1];
for (int j = 1; j <= len; ++j)
sum[j] = hashmp[gethash(1, j)];
sum[0] = 0;
for (int j = 1; j <= len; ++j)
sum[nex[j]] -= sum[j];
for (int j = 1; j <= len; ++j)
ans = (ans + sum[j] % MOD * j % MOD * j % MOD) % MOD;
}
cout << ans << endl;
}
D-Duration
题意
给同一天的两个时间,计算相差多少秒。
思路
单位化成秒,相减即可。
代码
#include <bits/stdc++.h>
using namespace std;
int main()
{
int h1, m1, s1, h2, m2, s2;
while (scanf("%d:%d:%d", &h1, &m1, &s1) != EOF)
{
scanf("%d:%d:%d", &h2, &m2, &s2);
int S1 = h1 * 3600 + m1 * 60 + s1;
int S2 = h2 * 3600 + m2 * 60 + s2;
printf("%d\n", abs(S1 - S2));
}
return 0;
}
F-Fake Maxpooling
题意
给一个nm的矩阵A,\(A_{ij}\)=lcm(i,j),计算大小为kk的子矩阵中的最大值之和。
思路
通过滑动窗口可以求出矩阵A每行大小为k的子段的最大值,再求出新的矩阵每列大小为k的子串的最大值,得出的矩阵内即为答案所求。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int n, m, k, a;
pair<int, int> que[5001];
int ql, qr;
vector<int> v[5001];
int main()
{
while (scanf("%d%d%d", &n, &m, &k) != EOF)
{
for (int i = 0; i <= m; ++i)
v[i].clear();
ql = qr = 0;
que[ql].first = 0x3f3f3f3f;
for (int i = 1; i <= m; ++i)
{
ql = qr = 0;
for (int j = 1; j <= k; ++j)
{
a = j * i / __gcd(j, i);
while (ql <= qr && que[qr].first <= a)
qr--;
que[++qr] = {a, j};
}
ql++;
for (int j = k + 1; j <= n; ++j)
{
v[i].push_back(que[ql].first);
a = j * i / __gcd(j, i);
while (ql <= qr && j - que[ql].second >= k)
ql++;
while (ql <= qr && que[qr].first <= a)
qr--;
que[++qr] = {a, j};
}
v[i].push_back(que[ql].first);
}
for (int i = 0; i <= n - k; ++i)
{
ql = qr = 0;
for (int j = 1; j <= k; ++j)
{
while (ql <= qr && que[qr].first <= v[j][i])
qr--;
que[++qr] = {v[j][i], j};
}
ql++;
for (int j = k + 1; j <= m; ++j)
{
v[0].push_back(que[ql].first);
while (ql <= qr && j - que[ql].second >= k)
ql++;
while (ql <= qr && que[qr].first <= v[j][i])
qr--;
que[++qr] = {v[j][i], j};
}
v[0].push_back(que[ql].first);
}
ll ans = 0;
for (auto i:v[0])
ans += i;
printf("%lld\n", ans);
}
return 0;
}
注意事项
时间够用就可以,不必为了追求时间而过分以空间换时间,有时会需要以时间换空间。

浙公网安备 33010602011771号