[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里插入。这样一定包含了任意一条路径。因为一条路径可以看做从某一个根节点出发的路径的子路径。
最后在后缀自动机上求一下本质不同的子串数目就好了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
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;
}
优质内容筛选与推荐>>
1、Flash/Flex学习笔记(24):粒子效果
2、如何手动搭建vnpy环境
3、从SAP最佳业务实践看企业管理(37)-SD-自库存销售
4、观点|Facebook田渊栋盛赞DeepMind最新围棋论文:方法干净标准,结果好
5、Linux入门很简单


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号