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;
}

注意事项

时间够用就可以,不必为了追求时间而过分以空间换时间,有时会需要以时间换空间。

posted @ 2020-07-22 16:15  训练日记  阅读(72)  评论(0)    收藏  举报