解:SAM + 线段树合并 + DFS序。

姓和名之间插入特殊字符,转化为下题:

给定串集合S,T,问S中每个串包含了T中的几个串?T中每个串被多少个S中的串包含?

解:对S建广义SAM,并线段树合并维护每个节点有多少串。

T中每个串在S的sam上跑,如果没能跑完就被包含0次。否则答案就是到达的节点上的串数。第二问解决。

标记T中每个串最后到达的节点。S中每个串跑S的sam会得到若干个点。统计这些点到根路径的并集上的标记个数即可。

按DFS序排序,加上每个节点到根路径的贡献,减去相邻节点lca到根路径的贡献。第一问解决。

 #include <bits/stdc++.h>

 const int N = , M = ;

 struct Edge {
int nex, v;
}edge[N]; int tp; std::map<int, int> tr[N];
int fail[N], len[N], tot = , e[N], n, m, siz[N], ed[N],
stk[N], top, num2, pos2[N], ST[N << ][], pw[N << ], d[N];
int ls[M], rs[M], num, sum[M], rt[N];
std::vector<int> str[N]; inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
} inline bool cmp(const int &a, const int &b) {
return pos2[a] < pos2[b];
} void insert(int p, int l, int r, int &o) {
if(!o) o = ++num;
if(l == r) {
sum[o] = ;
return;
}
int mid = (l + r) >> ;
if(p <= mid) insert(p, l, mid, ls[o]);
else insert(p, mid + , r, rs[o]);
sum[o] = sum[ls[o]] + sum[rs[o]];
return;
} int merge(int x, int y) {
if(!x || !y) return x | y;
int o = ++num;
ls[o] = merge(ls[x], ls[y]);
rs[o] = merge(rs[x], rs[y]);
if(!ls[o] && !rs[o]) sum[o] = ;
else sum[o] = sum[ls[o]] + sum[rs[o]];
return o;
} int ask(int L, int R, int l, int r, int o) {
if(!o) return ;
if(L <= l && r <= R) return sum[o];
int mid = (l + r) >> , ans = ;
if(L <= mid) ans += ask(L, R, l, mid, ls[o]);
if(mid < R) ans += ask(L, R, mid + , r, rs[o]);
return ans;
} void DFS_1(int x) {
pos2[x] = ++num2;
ST[num2][] = x;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
d[y] = d[x] + ;
DFS_1(y);
ST[++num2][] = x;
rt[x] = merge(rt[x], rt[y]);
}
return;
} inline void prework() {
for(int i = ; i <= num2; i++) pw[i] = pw[i >> ] + ;
for(int j = ; j <= pw[num2]; j++) {
for(int i = ; i + (j << ) - <= num2; i++) {
if(d[ST[i][j - ]] < d[ST[i + ( << (j - ))][j - ]])
ST[i][j] = ST[i][j - ];
else
ST[i][j] = ST[i + ( << (j - ))][j - ];
}
}
return;
} inline int lca(int x, int y) {
x = pos2[x];
y = pos2[y];
if(x > y) std::swap(x, y);
int t = pw[y - x + ];
if(d[ST[x][t]] < d[ST[y - ( << t) + ][t]])
return ST[x][t];
else
return ST[y - ( << t) + ][t];
} void DFS_2(int x) {
siz[x] += ed[x];
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
siz[y] = siz[x];
DFS_2(y);
}
return;
} inline int split(int p, int f) {
int Q = tr[p][f], nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = nQ;
//memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
tr[nQ] = tr[Q];
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
return nQ;
} inline int insert(int p, int f, int id) {
int np;
if(tr[p].count(f)) {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
np = Q;
}
else {
np = split(p, f);
}
insert(id, , n, rt[np]);
return np;
}
np = ++tot;
len[np] = len[p] + ;
while(p && !tr[p].count(f)) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
fail[np] = split(p, f);
}
}
insert(id, , n, rt[np]);
return np;
} void out(int l, int r, int o) {
if(!o) return;
if(l == r) {
printf("%d ", r);
return;
}
int mid = (l + r) >> ;
out(l, mid, ls[o]);
out(mid + , r, rs[o]);
return;
} inline void clear() {
for(int i = ; i <= tot; i++) {
e[i] = len[i] = fail[i] = rt[i] = ;
tr[i].clear();
}
for(int i = ; i <= num; i++) {
ls[i] = rs[i] = sum[i] = ;
}
tp = num = ;
tot = ;
return;
} int main() {
scanf("%d%d", &n, &m);
for(int i = ; i <= n; i++) {
int k, x, p = ;
scanf("%d", &k);
for(int j = ; j <= k; j++) {
scanf("%d", &x);
str[i].push_back(x);
p = insert(p, x, i);
}
str[i].push_back(-);
p = insert(p, -, i);
scanf("%d", &k);
for(int j = ; j <= k; j++) {
scanf("%d", &x);
str[i].push_back(x);
p = insert(p, x, i);
}
}
/// build
for(int i = ; i <= tot; i++) {
//printf("add %d %d \n", fail[i], i);
add(fail[i], i);
}
DFS_1(); for(int i = ; i <= m; i++) {
int k, x, p = , fd = , ans = ;
scanf("%d", &k);
for(int j = ; j <= k; j++) {
scanf("%d", &x);
if(!tr[p].count(x)) fd = ;
else p = tr[p][x];
}
if(!fd) {
ans = sum[rt[p]];
ed[p]++;
}
printf("%d\n", ans);
} DFS_2();
prework(); for(int i = ; i <= n; i++) {
int p = ; top = ;
for(int j = ; j < (int)str[i].size(); j++) {
int x = str[i][j];
p = tr[p][x];
stk[++top] = p;
}
std::sort(stk + , stk + top + ,cmp);
top = std::unique(stk + , stk + top + ) - stk - ;
int ans = ;
for(int j = ; j <= top; j++) {
ans += siz[stk[j]];
if(j < top) ans -= siz[lca(stk[j], stk[j + ])];
}
printf("%d ", ans);
} return ;
}

AC代码

AC自动机解法

洛谷P2336 喵星球上的点名的更多相关文章

  1. 洛谷 P2336 [SCOI2012]喵星球上的点名 解题报告

    P2336 [SCOI2012]喵星球上的点名 题目描述 a180285 幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有 \(N\) 个喵星人,每个喵星人的 ...

  2. 洛咕 P2336 [SCOI2012]喵星球上的点名

    洛咕 P2336 [SCOI2012]喵星球上的点名 先求出SA和height,一个点名串对应的就是一段区间,还有很多个点,就转化成了 有很多个区间,很多个点集,对每个区间计算和多少个点集有交,对每个 ...

  3. P2336 [SCOI2012]喵星球上的点名(后缀自动机+莫队+dfs序)

    P2336 [SCOI2012]喵星球上的点名 名字怎么存?显然是后缀自动机辣 询问点到多少个喵喵喵其实就是 查询后缀自动机上parent树的一个子树 于是我们考虑莫队 怎么树上莫队呢 我们用dfs序 ...

  4. BZOJ 2754: [SCOI2012]喵星球上的点名 [后缀数组+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1906  Solved: 839[Submit][St ...

  5. 【BZOJ2754】[SCOI2012]喵星球上的点名

    [BZOJ2754][SCOI2012]喵星球上的点名 题面 bzoj 洛谷 题解 这题有各种神仙做法啊,什么暴力\(AC\)自动机.\(SAM\)等等五花八门 我这个蒟蒻在这里提供一种复杂度正确且常 ...

  6. BZOJ_2754__[SCOI2012]_喵星球上的点名_(暴力+后缀数组)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2754 给出n个姓名串和m个点名串.求每个点名串在多少人的姓名中出现过(在名中出现或在姓中出现, ...

  7. BZOJ 2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 649  Solved: 305[Submit][Sta ...

  8. BZOJ2754: [SCOI2012]喵星球上的点名

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 680  Solved: 314[Submit][Sta ...

  9. BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 1902  Solved: 837[Submit][St ...

随机推荐

  1. git上传本地代码到github

      1.(先进入项目文件夹)通过命令 git init 把这个目录变成git可以管理的仓库 git init 2.把文件添加到版本库中,使用命令 git add .添加到暂存区里面去,不要忘记后面的小 ...

  2. 小程序获取当前页面URL

    var pages = getCurrentPages() //获取加载的页面 var currentPage = pages[pages.length-1] //获取当前页面的对象 var url ...

  3. 使用urllib2+re爬取web网站

    应用1,使用urllib2+re爬取淘宝网指定页面的所有图片

  4. C#使用MemoryStream类读写内存

    MemoryStream和BufferedStream都派生自基类Stream,因此它们有很多共同的属性和方法,但是每一个类都有自己独特的用法.这两个类都是实现对内存进行数据读写的功能,而不是对持久性 ...

  5. Lodop打印旋转180度 倒着打

    方法1:打印出来后,直接把纸张倒过来.如果本身是白纸,打印机出纸内容是倒着的,可以打出来后手动倒着把纸张正过来.如果本身不是白纸,需要打印的纸张上有背景,调整进纸方向.(如果是卷纸,卷纸背景是反的,查 ...

  6. 3.ansible-iventory的写法和基本变量

    ansible的配置文件一点要多考虑,有些设定比如ssh端口啊用户啊线程啊都尽量在里面调节好iventory的话/etc/ansible/hosts 里面可以使用正则匹配ansible从invento ...

  7. name设置id的方式 解决多个单选域冲突现象 同时有利于从动态网页取值

  8. CSS3 flexbox 布局 ---- flex 容器属性介绍

    flexbox布局是CSS3中新增的属性,它可以很轻松地帮我们解决掉一些常见的布局问题,比如导航栏. 我们用普通的方法写导航栏,通常会在ul, li 结构写好后,让li 元素左浮动,然后再给ul 清浮 ...

  9. BZOJ4912 SDOI2017天才黑客(最短路+虚树)

    容易想到把边当成点重建图跑最短路.将每条边拆成入边和出边,作为新图中的两个点,由出边向入边连边权为原费用的边.对于原图中的每个点,考虑由其入边向出边连边.直接暴力两两连边当然会被卡掉,注意到其边权是t ...

  10. 【妙味课堂】JS热身课后习题

    <!--*** @Author: wyy* @Date: 2018-04-15 17:36:35* @Email: 2752154874@qq.com* @Last Modified by: w ...