@codeforces - [email protected] Asterisk Substrings

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一个包含 n 个小写字母的字符串 s,用 s 生成 n 个串 t1...n,其中 ti 等于字符串 s 将第 i 个字符替换为 * 得到的字符串。

特别注意:这里的 * 只是一个字符,并不具有其他含义(如通配符)。

求有多少字符串,在 {s, t1, t2, ..., tn} 中作为至少一个字符串的子串出现。

戳我查看原题o.o

@[email protected]

不包含 * 的子串即 s 的子串,经典问题。因此,我们只需要考虑 ti 中包含 * 的子串。

考虑 ti 中一个包含 * 的子串,总可以用 s[1...i-1] 的一个后缀 + * + s[i+1...n] 的一个前缀来表示。
因为 * 是固定的,所以又可以用一个二元组 (s[1...i-1]的某后缀, s[i+1...n]的某前缀) 表示一个含 * 的子串。

考虑建出正着建一遍后缀自动机 sam1,反着建一遍后缀自动机 sam2。
则 s[1...i-1] 在 sam1 中对应的结点到根的路径上的所有结点都可以与 s[i+1...n] 在 sam2 中对应的结点到根的路径上的所有结点结合成二元组。

接下来怎么统计?考虑 sam1 中的每个点,求出它的子树内所有结点对应到 sam2 上的链的并集,这个交集就是该点的贡献。
链交集有一个众所周知的做法:将点按照 dfs 序来排序,用所有点到根的链信息减去 dfs 序相邻两个点的 lca 到根的链信息。
因为要求子树内所有点的链交集,不难想到线段树合并。然后发现线段树合并的确可以维护(每次 pushup 时考虑左儿子的最右边的点与右儿子的最左边的点的 lca)。

注意一下空串是合法的。
时间复杂度 O(nlogn)(如果倍增求 lca 就是 O(nlog^2n))。

@accepted [email protected]

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXN = 200000;

#define rep(G, x) for(Graph::edge *p=G.adj[x];p;p=p->nxt)
struct Graph{
    struct edge{
        edge *nxt; int to;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
    Graph() {ecnt = edges;}
    void addedge(int u, int v) {
        edge *p = (++ecnt);
        p->to = v, p->nxt = adj[u], adj[u] = p;
        p = (++ecnt);
        p->to = u, p->nxt = adj[v], adj[v] = p;
    }
}G1, G2;

struct SAM{
    int fa[MAXN + 5], len[MAXN + 5], ch[26][MAXN + 5];
    int root, ncnt, lst;
    SAM() {root = ncnt = lst = 1; len[0] = -1;}
    void copy(int nq, int q) {
        for(int i=0;i<26;i++)
            ch[i][nq] = ch[i][q];
        fa[nq] = fa[q], len[nq] = len[q];
    }
    int extend(int x) {
        int p = lst, nw = (++ncnt);
        len[nw] = len[lst] + 1, lst = nw;
        while( p && ch[x][p] == 0 )
            ch[x][p] = nw, p = fa[p];
        if( !p ) fa[nw] = root;
        else {
            int q = ch[x][p];
            if( len[p] + 1 == len[q] )
                fa[nw] = q;
            else {
                int nq = (++ncnt); copy(nq, q);
                len[nq] = len[p] + 1, fa[q] = fa[nw] = nq;
                while( p && ch[x][p] == q )
                    ch[x][p] = nq, p = fa[p];
            }
        }
        return nw;
    }
}S1, S2;

int cnt[MAXN + 5], fir[MAXN + 5], dfn[2*MAXN + 5], dep[MAXN + 5], dcnt;
void dfs1(int x, int f) {
    dfn[++dcnt] = x, fir[x] = dcnt, dep[x] = dep[f] + 1;
    rep(G2, x) {
        if( p->to == f ) continue;
        dfs1(p->to, x), dfn[++dcnt] = x;
    }
    cnt[x] = S2.len[x] + 1;
}
int lg[2*MAXN + 5], st[20][2*MAXN + 5];
void get_st() {
    for(int i=1;i<=dcnt;i++) st[0][i] = dfn[i];
    for(int i=2;i<=dcnt;i++) lg[i] = lg[i >> 1] + 1;
    for(int j=1;j<20;j++) {
        int t = (1 << (j - 1));
        for(int i=1;i+t<=dcnt;i++)
            st[j][i] = (dep[st[j-1][i]] < dep[st[j-1][i+t]] ? st[j-1][i] : st[j-1][i+t]);
    }
}
int lca(int x, int y) {
    int l = fir[x], r = fir[y];
    if( l > r ) swap(l, r);
    int k = lg[r - l + 1], p = (1 << k);
    return (dep[st[k][l]] < dep[st[k][r-p+1]] ? st[k][l] : st[k][r-p+1]);
}

struct segtree{
    struct node{
        node *ch[2];
        int lx, rx; ll res;
    }pl[20*MAXN + 5], *NIL, *ncnt;
    segtree() {
        NIL = ncnt = pl;
        NIL->ch[0] = NIL->ch[1] = NIL;
        NIL->lx = NIL->rx = NIL->res = 0;
    }
    node *newnode() {
        ncnt++;
        ncnt->ch[0] = ncnt->ch[1] = NIL;
        ncnt->lx = ncnt->rx = ncnt->res = 0;
        return ncnt;
    }
    void pushup(node *x) {
        x->lx = (x->ch[0] == NIL ? x->ch[1]->lx : x->ch[0]->lx);
        x->rx = (x->ch[1] == NIL ? x->ch[0]->rx : x->ch[1]->rx);
        x->res = x->ch[0]->res + x->ch[1]->res;
        if( x->ch[0] != NIL && x->ch[1] != NIL )
            x->res -= cnt[lca(dfn[x->ch[0]->rx], dfn[x->ch[1]->lx])];
    }
    void insert(node *&rt, int l, int r, int p) {
        if( rt == NIL ) rt = newnode();
        if( l == r ) {
            rt->lx = rt->rx = p, rt->res = cnt[dfn[p]];
            return ;
        }
        int m = (l + r) >> 1;
        if( p <= m ) insert(rt->ch[0], l, m, p);
        else insert(rt->ch[1], m + 1, r, p);
        pushup(rt);
    }
    node *merge(node *rt1, node *rt2) {
        if( rt1 == NIL ) return rt2;
        if( rt2 == NIL ) return rt1;
        rt1->ch[0] = merge(rt1->ch[0], rt2->ch[0]);
        rt1->ch[1] = merge(rt1->ch[1], rt2->ch[1]);
        pushup(rt1); return rt1;
    }
}T;
segtree::node *rt[MAXN + 5];

ll ans;
void dfs2(int x, int f) {
    rep(G1, x) {
        if( p->to == f ) continue;
        dfs2(p->to, x);
        rt[x] = T.merge(rt[x], rt[p->to]);
    }
    ans += rt[x]->res * (S1.len[x] - S1.len[f]);
}

char s[MAXN + 5]; int n;
int pos1[MAXN + 5], pos2[MAXN + 5];
ll get_num() {
    ll ret = 0;
    for(int i=1;i<=S1.ncnt;i++)
        ret += S1.len[i] - S1.len[S1.fa[i]];
    return ret;
}
int main() {
    scanf("%s", s + 1), n = strlen(s + 1);
    for(int i=1;i<=n;i++) pos1[i] = S1.extend(s[i] - 'a');
    for(int i=n;i>=1;i--) pos2[i] = S2.extend(s[i] - 'a');
    pos1[0] = pos2[n+1] = 1;
    for(int i=2;i<=S1.ncnt;i++) G1.addedge(S1.fa[i], i);
    for(int i=2;i<=S2.ncnt;i++) G2.addedge(S2.fa[i], i);
    ans = get_num();
    dfs1(1, 0), get_st();
    for(int i=0;i<=S1.ncnt;i++) rt[i] = T.NIL;
    for(int i=1;i<=n;i++) T.insert(rt[pos1[i-1]], 1, dcnt, fir[pos2[i+1]]);
    dfs2(1, 0);
    printf("%lld\n", ans);
}

@[email protected]

F 题好像比 E 题简单来着。。。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12093227.html

时间: 2024-08-01 02:45:32

@codeforces - [email protected] Asterisk Substrings的相关文章

@codeforces - [email&#160;protected] Mashmokh&#39;s Designed Problem

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,每个点的儿子是有序的. 现给定 m 次操作,每次操作是下列三种中的一种: (1)给定 u, v,询问 u, v 之间的距离. (2)给定 v, h,断开 v 到父亲的边,将 v 这棵子树加入到它的第 h 个祖先的最后一个儿子. (3)给定 k,询问在当前这棵树上

@codeforces - [email&#160;protected] T-Shirts

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 件 T-shirt,第 i 件 T-shirt 有一个 ci 和 qi,分别表示费用与质量. 同时有 k 个顾客,第 j 个顾客准备了 bj 的金钱去购买 T-shirt. 每个顾客的购买策略是相同的: 他会买他的资金范围内 q 值最大的一件,如果有多个选 c 最小的一件,每种

@codeforces - [email&#160;protected] Oleg and chess

目录 @description - [email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @description - [email protected] 给定一个 n*n 的棋

@codeforces - [email&#160;protected] Lucky Tickets

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 已知一个数(允许前导零)有 n 位(n 为偶数),并知道组成这个数的数字集合(并不一定要把集合内的数用完).求有多少种可能,使得这个数前半部分的数位和等于后半部分的数位和. 模 998244353. input 第一行两个整数:n k.表示这个数的位数以及组成这个数的数字集合大小.2

@codeforces - [email&#160;protected] Bandit Blues

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 求有多少个长度为 n 的排列,从左往右遍历有 a 个数比之前遍历的所有数都大,从右往左遍历有 b 个数比之前遍历的所有数都大. 模 998244323. input 一行三个整数 n

@codeforces - [email&#160;protected] Vus the Cossack and a Field

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的 01 矩阵,通过这个矩阵生成一个无穷矩阵,具体操作如下: (1)将这个矩阵写在左上角. (2)将这个矩阵每位取反写在右上角. (3)将这个矩阵每位取反写在左下角. (4)将这个矩阵写在右下角. (5)将得到的矩阵再作为初始矩阵,重复这些操作. 比如对于初始矩阵:

@codeforces - [email&#160;protected] Big Problems for Organizers

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个点连成一棵树,经过每条边需要花费 1 个单位时间. 现给出 m 次询问,每次询问给出两个点,需要求所有点同时出发,最终所有点到达这两个点之一的最小花费时间. input 第一行包含一个整数 n (2?≤?n?≤?100000) ,表示点数. 接下来 n-1 行每行两个 1~n 的

@codeforces - [email&#160;protected] Strongly Connected Tournament

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 个选手参加了一场竞赛,这场竞赛的规则如下: 1.一开始,所有选手两两之间独立进行比赛(没有平局). 2.主办方将胜者向败者连边形成 n 个点的竞赛图. 3.主办方对这个竞赛图进行强连通分量缩点. 4.每一个强连通分量内部的选手重复步骤 1~3,直到每一个强连通分量内只剩一个选手.

@codeforces - [email&#160;protected] Rotate Columns (hard version)

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个 n*m 的矩阵 A. 定义一次操作为将矩阵的某一列竖着循环移位,你可以对任意列做任意次操作. 定义 ri 为第 i 行的最大值,最大化 r1 + r2 + ... + rn. Input 第一行一个整数 t (1≤t≤40),表示数据组数. 每组数据第一行包含两个整数 n m