• [ZJOI2015]诸神眷顾的幻想乡


    嘟嘟嘟


    这题除了暴力我就不会了,感觉得用SAM,但是又和普通的SAM不一样。
    看了题解才知道,这东西叫广义后缀自动机。
    就是解决例如多个串的本质不同的子串的个数这样的问题。
    做法就是每插入完一个串,就重新从根节点开始插入另一个字符串。(但一直只有一个SAM)


    对于这道题,可以理解为在trie上建SAM。做法就是在trie上dfs,孩子节点从父亲节点在SAM中的位置插入,代码就是这样:

    In void dfs(int now, int _f, int p)
    {
      p = S.insert(col[now], p);
      for(int i = head[now], v; ~i; i = e[i].nxt)
        if((v = e[i].to) ^ _f) dfs(v, now, p);
    }
    

    因为最多只有20个叶节点,所以我们以每一个叶节点为根,跑一边trie,同时往SAM里插入。这样一定包含了任意一条路径。因为一条路径可以看做从某一个根节点出发的路径的子路径。 最后在后缀自动机上求一下本质不同的子串数目就好了。 ```c++ #include #include #include #include #include #include #include #include #include #include using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define In inline typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 1e5 + 5; inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) last = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); }

    int n, m, col[maxn], du[maxn];
    struct Edge
    {
    int nxt, to;
    }e[maxn << 1];
    int head[maxn], ecnt = -1;
    In void addEdge(int x, int y)
    {
    e[++ecnt] = (Edge){head[x], y};
    head[x] = ecnt;
    }

    struct Sam
    {
    int cnt;
    int tra[maxn * 40][11], len[maxn * 40], link[maxn * 40];
    In void init() {link[cnt = 0] = -1;}
    In int insert(int x, int las)
    {
    int now = ++cnt, p = las;
    len[now] = len[las] + 1;
    while(~p && !tra[p][x]) tra[p][x] = now, p = link[p];
    if(p == -1) link[now] = 0;
    else
    {
    int q = tra[p][x];
    if(len[q] == len[p] + 1) link[now] = q;
    else
    {
    int clo = ++cnt;
    memcpy(tra[clo], tra[q], sizeof(tra[q]));
    len[clo] = len[p] + 1;
    link[clo] = link[q], link[q] = link[now] = clo;
    while(~p && tra[p][x] == q) tra[p][x] = clo, p = link[p];
    }
    }
    return now;
    }
    In ll calc()
    {
    ll ret = 0;
    for(int i = 1; i <= cnt; ++i) ret += len[i] - len[link[i]];
    return ret;
    }
    }S;

    In void dfs(int now, int _f, int p)
    {
    p = S.insert(col[now], p);
    for(int i = head[now], v; ~i; i = e[i].nxt)
    if((v = e[i].to) ^ _f) dfs(v, now, p);
    }

    int main()
    {
    Mem(head, -1);
    n = read(), m = read();
    for(int i = 1; i <= n; ++i) col[i] = read();
    for(int i = 1; i < n; ++i)
    {
    int x = read(), y = read();
    addEdge(x, y), addEdge(y, x);
    ++du[x], ++du[y];
    }
    S.init();
    for(int i = 1; i <= n; ++i) if(du[i] == 1) dfs(i, 0, 0);
    write(S.calc()), enter;
    return 0;
    }

  • 相关阅读:
    mysql 时间函数
    Excel名称管理
    Unicode中文和特殊字符的编码范围
    带有历史数据置顶的id列表查询
    汉字表示范围
    ASP.NET模拟http进行GET/POST请求
    ASP.NET AES-128-CBC加密解密(与php通讯)
    dapper.net 获取分页存储过程返回的多结果集
    微信网页版抓包登录
    js添加/移除/阻止事件
  • 原文地址:https://www.cnblogs.com/mrclr/p/10465704.html
Copyright © 2020-2023  润新知