"蔚来杯"2022牛客暑期多校训练营1 B Spirit Circle Observation

Spirit Circle Observation

https://ac.nowcoder.com/acm/contest/33186/B

B Spirit Circle Observation

感谢@RedreamMer 巨佬 找到了我题解中的致命错误

Updata by 2022/7/20 15:29

构造的数据

#include <bits/stdc++.h>
using namespace std;
int a = 1e5;
int main() {
    freopen("a.in", "w", stdout);
    string s;
    for (int i = 1; i <= a; ++i) s.push_back('0');
    s.push_back('1');
    for (int i = 1; i <= a; ++i) s.push_back('0');
    for (int i = 1; i <= a; ++i) s.push_back('9');
    std::cout << s.size() << '\n' << s << '\n';
    return 0;
}

SAMSAM求出endposendpos然后枚举最后一位不是99的情况

我们枚举的是上面一个节点,贡献是

i=1totj=08endpos(ch[i][j])×endpos(ch[i][j+1])×(len[i]len[Link[i])\sum_{i=1}^{tot}\sum_{j=0}^{8}endpos(ch[i][j])\times endpos(ch[i][j+1])\times (len[i]-len[Link[i])

接下来枚举A+c+999A+c+99\dots9B+(char)(c+1)+000B+(char)(c+1)+00\dots0配对的情况

就是SAMSAM上枚举每个节点下方的子节点

如枚举到节点 ii 然后就枚举o=ch[i][j],p=ch[i][j+1](0j8)o=ch[i][j],p=ch[i][j+1](0\le j\le 8) 然后就oo一直走99的那一边,pp一直走00的一边,边走边统计,计算方式如上

这样是暴力会被卡成O(n2)O(n^2)

alt

官方题解给出了明确的说法,p1,p2p1,p2 这样的关键点对只有nn个,ch[p1][9],ch[p2][0]ch[p1][9],ch[p2][0]最多也只有nn个,我们就用mapmap 暴力存下所有点对及下方字符串的endpos(x)×endpos(y)endpos(x)\times endpos(y) 然后计算即可

#include <bits/stdc++.h>
typedef long long LL;

const int N = 8e5 + 1000;
int ch[N][10], last = 1, siz[N], Link[N], tot = 1, len[N];
char s[N];
std::vector<int> G[N];

void insert(int c) {
    int o = ++ tot, p = last; len[o] = len[p] + 1; siz[o] = 1;
    for (; p && ch[p][c] == 0; p = Link[p]) ch[p][c] = o;
    if (!p) Link[o] = 1;
    else {
        int q = ch[p][c];
        if (len[q] == len[p] + 1) Link[o] = q;
        else {
            int nq = ++ tot;
            Link[nq] = Link[q];
            len[nq] = len[p] + 1;
            Link[q] = Link[o] = nq;
            memcpy(ch[nq], ch[q], sizeof(ch[nq]));
            for (;ch[p][c] == q; p = Link[p]) ch[p][c] = nq;
        }
    }
    last = o;
}

void dfs(int x) {
    for (auto y : G[x]) {
        dfs(y);
        siz[x] += siz[y];
    }
}

void Get() {
    for (int i = 2; i <= tot; ++ i) G[Link[i]].push_back(i);
    dfs(1);
}

std::map<std::pair<int, int>, LL> mp;

LL Get(int x, int y) {
    if (!x || !y) return 0;
    if (mp.find({x, y}) != mp.end()) return mp[{x, y}];
    return mp[{x, y}] += (LL)Get(ch[x][9], ch[y][0]) + (LL)siz[x] * siz[y];
}

void work() {
    LL ans = 0; siz[0] = 0; len[0] = -1;
    for (int i = 1; i <= tot; ++ i) {
        for (int j = 0; j < 9; ++ j) {
            int o = ch[i][j], p = ch[i][j + 1];
            ans += (LL)(siz[o] * siz[p] + Get(ch[o][9], ch[p][0])) * (len[i] - len[Link[i]]);
        }
    }
    std::cout << ans << std::endl;
}

int main() {
    int n;
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++ i) insert(s[i] - 48);
    Get(); work();
    return 0;
}


原代码
#include <bits/stdc++.h>
typedef long long LL;

const int N = 8e5 + 1000;
int ch[N][10], last = 1, siz[N], Link[N], tot = 1, len[N];
char s[N];
std::vector<int> G[N];

void insert(int c) {
    int o = ++ tot, p = last; len[o] = len[p] + 1; siz[o] = 1;
    for (; p && ch[p][c] == 0; p = Link[p]) ch[p][c] = o;
    if (!p) Link[o] = 1;
    else {
        int q = ch[p][c];
        if (len[q] == len[p] + 1) Link[o] = q;
        else {
            int nq = ++ tot;
            Link[nq] = Link[q];
            len[nq] = len[p] + 1;
            Link[q] = Link[o] = nq;
            memcpy(ch[nq], ch[q], sizeof(ch[nq]));
            for (;ch[p][c] == q; p = Link[p]) ch[p][c] = nq;
        }
    }
    last = o;
}

void dfs(int x) {
    for (auto y : G[x]) {
        dfs(y);
        siz[x] += siz[y];
    }
}

void Get() {
    for (int i = 2; i <= tot; ++ i) G[Link[i]].push_back(i);
    dfs(1);
}

void work() {
    LL ans = 0; siz[0] = 0; len[0] = -1;
    for (int i = 1; i <= tot; ++ i) {
        for (int j = 0; j < 9; ++ j) {
            int o = ch[i][j], p = ch[i][j + 1];
            while (o && p) {
                ans += ((LL)siz[o] * siz[p]) * (LL)(len[i] - len[Link[i]]);
                o = ch[o][9];
                p = ch[p][0];
            }
        }
    }
    std::cout << ans << std::endl;
}

int main() {
    int n;
    scanf("%d%s", &n, s + 1);
    for (int i = 1; i <= n; ++ i) insert(s[i] - 48);
    Get(); work();
    return 0;
}
全部评论
复杂度是假的,可以卡成 n ^ 2
1 回复 分享
发布于 2022-07-20 09:57
感谢 @zhoukangyang 神仙的数据/bx/bx/bx
1 回复 分享
发布于 2022-07-20 09:58
int a = 1e5; signed main() { freopen("in.in", "w", stdout); ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); string s; for(int i = 1; i <= a; ++i) s.push_back('0'); s.push_back('1'); for(int i = 1; i <= a; ++i) s.push_back('0'); for(int i = 1; i <= a; ++i) s.push_back('9'); cout << s.size() << '\n' << s << '\n'; return 0; }
1 回复 分享
发布于 2022-07-20 09:59
assert 一下可以发现一个点作为 o和p 被访问的最大次数甚至是超过 100 的。这个做法实际上是暴力(但看起来不是很好卡
点赞 回复 分享
发布于 2022-07-19 14:18
请问一下 map 做法里 {x, y} 点对的个数是如何保证的呢?题解里指出了关键点个数 O(n),但是关键点的对数却不一定有保证,这个细节还烦请大佬赐教
点赞 回复 分享
发布于 2022-07-22 20:19

相关推荐

不愿透露姓名的神秘牛友
11-22 12:00
点赞 评论 收藏
分享
评论
9
收藏
分享
牛客网
牛客企业服务