喵星球上的点名

一道据传言有多种解法的题(不过大多是因为数据太弱过的)。先用AC自动机搞了一上午,无果;看了题解,后缀自动机+莫队?正好是我最喜欢的算法之一+正在学习的算法,就这个了!然后由于广义自动机的lastlastlast标记有个地方忘了初始化。。。然后在大物课上调了三节课,洛谷一页都是我,hhh

题意

有NNN个同学,名字包含姓和名;然后老师要点MMM次名,某次点名若是某个同学的姓或者名的子串,则这位同学要答到。求:

  1. 每次点名会有多少同学答到
  2. MMM次点名后每位同学分别会答到多少次

思路后缀自动机+dfsdfsdfs序+莫队

  1. 由于涉及多字符串,当然是用广义后缀自动机搞呀(也可以在字符串之间插入不涉及的数字,并且用mapmapmap存转移边);同时记得在建后缀自动机时给每个节点表明是属于哪个人的,毕竟后面要分别统计每个人的答到次数;还有虚点的问题,这里的处理其实比较随意,你既可以当做是当前同学的,也可以当做是之前同学的,甚至可以当做一个不会出现的同学的(比如默认为000),这点是非常有趣的。
  2. 询问中需要知道每次点名会有多少人答到,显然可以直接在后缀自动机上跑这个点名的字符串,找到那个节点后,属于这个节点的整棵parentparentparent treetreetree子树的同学都是要答到的。
  3. 这时我们容易想到处理出parentparentparent treetreetree的dfsdfsdfs序,这样属于某个节点的整棵子树在dfsdfsdfs序上都是连续的一段,当我们想要知道里面有多少个不同的同学的姓名时,也就是询问区间不同种类数字个数!就可以直接莫队处理啦(也可以像HH的项链那样用树状数组处理啦)。
  4. 至此,第一个问题就轻松解决了,那么第二问呢?第二个问题可以等效为某个节点被多少询问区间所包含!注意到在使用莫队的过程中,每个节点总是在不断地进入,出去。那么,在进入之后,出来之前发生了什么呢?显然这个节点(同学)在不断地贡献答案,也就是他在这段时间内一直被包含!因此,在端点移动过程中,每次有新的未出现的节点进入区间,就记录这个节点的进入时间,当他出去的时候,就统计答案。这样,每个同学被点到的次数就被分成了多段连续的时间,加在一起就是总的次数!
  5. 补充:第二问同样可以用树状数组处理:将区间放在树状数组上,每当到达区间左端点时,bit[L]+1bit[L]+1bit[L]+1,到达区间右端点时,bit[L]−1bit[L]-1bit[L]−1;没当到达某个人的姓名时(两部分),对于每个人的两个字符串,靠左边的统计答案为ans=qsum(pos1)ans=qsum(pos1)ans=qsum(pos1),靠右边的统计为ans+=qsum(pos2)−qsum(pos1)ans+=qsum(pos2)-qsum(pos1)ans+=qsum(pos2)−qsum(pos1);这样的处理就可以知道这个人总共被覆盖多少次啦!
  6. 妙哉!!!

代码

#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}const int maxn = 4e5+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;int N, M;
map<int,int> ch[maxn];
int len[maxn], fa[maxn], belong[maxn], last=1, sz=1;
int head[maxn], to[maxn], nxt[maxn], tot;
int siz[maxn], id[maxn], rk[maxn], dfn, vis[maxn], pre[maxn];
int cur, cntq, ans1[maxn], ans2[maxn], block;inline void add_edge(int u, int v) {++tot; to[tot]=v; nxt[tot]=head[u]; head[u]=tot;
}struct Q{int l, r, id;friend bool operator < (const Q &a, const Q &b) {if((a.l-1)/block!=(b.l-1)/block) return a.l<b.l;if((a.l-1)/block%2) return a.r>b.r;return a.r<b.r;}
}q[maxn];void add(int c, int ID) {int p=last, np=last=++sz;len[np]=len[p]+1, belong[np]=ID;for(; p&&!ch[p].count(c); p=fa[p]) ch[p][c]=np;if(!p) fa[np]=1;else {int q=ch[p][c];if(len[q]==len[p]+1) fa[np]=q;else {int nq=++sz; len[nq]=len[p]+1;fa[nq]=fa[q]; fa[np]=fa[q]=nq;ch[nq]=ch[q];for(; p&&ch[p][c]==q; p=fa[p]) ch[p][c]=nq;}}
}void dfs(int u) {rk[id[u]=++dfn]=u; siz[u]=1;for(int i=head[u]; i; i=nxt[i]) {dfs(to[i]); siz[u]+=siz[to[i]];}
}int main() {//ios::sync_with_stdio(false); cin.tie(0);N=read(), M=read();for(int i=1, L; i<=N; ++i) {last=1; L=read(); for(int j=1; j<=L; ++j) add(read(),i);last=1; L=read(); for(int j=1; j<=L; ++j) add(read(),i);}for(int i=2; i<=sz; ++i) add_edge(fa[i],i); dfs(1);for(int i=1; i<=M; ++i) {int L=read(), now=1;for(int j=1; j<=L; ++j) now=ch[now][read()];if(now) q[++cntq]=(Q){id[now],id[now]+siz[now]-1,i};}block=ceil(sqrt(dfn));sort(q+1,q+1+cntq);int l=1, r=0; //不用把第一个区间单独处理,莫队其实会自动处理好的,真妙!for(int i=1; i<=cntq; ++i) {while(l<q[i].l) {int p=belong[rk[l]];vis[p]--;if(!vis[p]&&p) cur--, ans2[p]+=i-pre[p];l++;}while(l>q[i].l) {--l;int p=belong[rk[l]];if(!vis[p]&&p) cur++, pre[p]=i;vis[p]++;}while(r<q[i].r) {++r;int p=belong[rk[r]];if(!vis[p]&&p) cur++, pre[p]=i;vis[p]++;}while(r>q[i].r) {int p=belong[rk[r]];vis[p]--;if(!vis[p]&&p) cur--, ans2[p]+=i-pre[p];r--;}ans1[q[i].id]=cur;}for(int i=1; i<=N; ++i) if(vis[i]) ans2[i]+=cntq+1-pre[i]; //别忘了有些同学还没有离开区间呀for(int i=1; i<=M; ++i) printf("%d\n", ans1[i]);for(int i=1; i<=N; ++i) printf("%d%c", ans2[i], " \n"[i==N]);
}

喵星球上的点名(后缀自动机+dfs序+莫队)相关推荐

  1. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

  2. BZOJ 2754 [SCOI2012]喵星球上的点名 (AC自动机、树状数组)

    吐槽: 为啥很多人用AC自动机暴力跳都过了?复杂度真的对么? 做法一: AC自动机+树状数组 姓名的问题,中间加个特殊字符连起来即可. 肯定是对点名串建AC自动机(map存儿子),然后第一问就相当于问 ...

  3. [SCOI2012]喵星球上的点名(后缀数组+莫队+ST表)

    传送门 这题是真的秀.一眼看下去感觉AC自动机很可做,第一个问比较好处理,dfs序即可搞定,可第二问有点抽象,目前对树形结构的知识点不足以支持我解决这个问题.所以舍弃AC自动机,用SA做. SA做法: ...

  4. [BZOJ2754]-[SCOI2012]喵星球上的点名-AC自动机+树状数组

    说在前面 感觉这题还是挺经典的 所以还是写了记录一下- 题目 BZOJ2754传送门 洛谷P2336传送门 看题可进传送门 题目-略长,概括起来有点麻烦 解法 读完这道题之后,可以发现实际上它就是要我 ...

  5. bzoj 2754 [SCOI2012]喵星球上的点名 后缀数组+莫队

    先把所有串按顺序放到一起,两个串间加非法字符隔开,求一个后缀数组. 然后对于询问,满足条件的子串在后缀数组上一定是连续一段区间. 这个区间的左右端点可以在读入的过程中二分求. 然后这个问题变成了多组询 ...

  6. bzoj2754JZOJ2834【SCOI2012】喵星球上的点名 AC自动机+STL

    题意比较复杂,就不说了. 感觉这题目拿来恶心人的..连输入都要花费好大功夫.表示这种题目经典套路啊,一般来说直接上AC自动机,暴力把大串往小串上贴,然后直接计算答案就行了,但是这数据大小简直了..算了 ...

  7. [BZOJ2754][SCOI2012]喵星球上的点名 后缀数组

    不科学啊...这题暴力可过...感觉所有串都是a就可以卡掉啊... 我的做法就是先把姓名串和询问串全部连在一起,并打上分隔符,并记录每个字符属于哪个串,求出SA.对于每个询问就从它所在的位置左右扫he ...

  8. 【BZOJ2754】【SCOI2012】喵星球上的点名(后缀数组)

    Description click me Solution 据说AC自动机可做?反正我使用后缀数组做的.. 把所有的串连在一起,用一个很大的数分割开来. 求出后缀数组,暴力地在height上找可以匹配 ...

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

    二次联通门 : BZOJ 2754: [SCOI2012]喵星球上的点名 /*BZOJ 2754: [SCOI2012]喵星球上的点名此题有N种做法...见到众dalao用各种奇怪的姿势AC此题..具 ...

最新文章

  1. vscode 远程linux 服务器开发
  2. Python开发基础总结之套接字+字符串+正则表达式
  3. 360导航源码php,114啦网址导航源码仿360网址导航最新版
  4. visual studio 阅读 linux-kernel
  5. CentOS系统下docker的基本命令
  6. 最让人心动的十大互联网界广告语+超笑评语
  7. 【BZOJ 2306】 2306: [Ctsc2011]幸福路径 (倍增floyd)
  8. ASP.NET中缓存(cache)的控制方法
  9. Java面试题--搜索(solrelasticsearch)
  10. Openwrt中安装njit-client(njit8021xclient)及使用方法
  11. npm切换到国内华为云的镜像
  12. SerDes知识详解
  13. [Axure RP9] Axure RP Prototyping: Noob to Master 学习笔记
  14. Internet协议的安全性
  15. 中科院软件所的毕业去向(硕+博)
  16. 《用微信测试公众号慰问你的好兄弟/姐妹》:用java简单实现微信公众号消息推送(入门且详细且有效)
  17. 公司测试员用例写得乱七八糟,测试总监制定了这份《测试用例编写规范》
  18. 小博的软件测试学习笔记(V1.0)
  19. 计算机一级表格减法,怎么把表格的数字全部加减
  20. 微软提前发新版音乐播放器 阻击苹果新iPod

热门文章

  1. 【JVM】GC垃圾回收(三)——零落成泥碾作尘,只有香如故
  2. 使用加速度计DIY自制基于Arduino手势控制的空中鼠标
  3. Node.js + Socket.io 实现一对一即时聊天
  4. 换脸上阵的路由界新面孔,联想云路由动手玩
  5. iPhone连接mac电脑连接时反复连接与断开情况的处理
  6. python123填充颜色_WPS表格中如何设置图案填充颜色透明度?,值得收藏
  7. C/C++笔试题(13)
  8. 前后端分离电商B2C模式之_后台_购物车
  9. XiaoHu日志 11/22~12/3
  10. 体验新一代Windows CE: Windows Embedded Compact 7