题目链接

题意:
有nnn个人,每个人有两个串,一个表示姓,一个表示名,这里用数字表示字符。有mmm次询问,对于每次询问,你要回答有多少个人的姓或者名至少有一个是给出的串的子串。最后再对于这nnn个人中的每一个人,回答这个mmm个串中的多少个串是这个人的姓或者名至少一个的子串。n&lt;=5e4,m&lt;=1e5,总长度&lt;=1e5,数值&lt;=1e4n&lt;=5e4,m&lt;=1e5,总长度&lt;=1e5,数值&lt;=1e4n<=5e4,m<=1e5,总长度<=1e5,数值<=1e4。

题解:
不会这个题,是看了题解的。(虽然我几乎没有什么不是看题解的题,但是这个题我似乎都不知道该怎么讲是怎么想到要用SA的。。)

我们先把所有串首尾相连,中间用一个大权值的字符隔开。然后对这个串做一次SA,并且求出rank、height。所有都连起来做SA的好处是一个串的所有子串都会成为它某一个后缀的前缀,那么一个询问串是否是一个串的子串,只需要判断是不是某一个后缀的前缀就可以了。

我们对于一个询问,会包含它的串应该是所有前缀包含它的后缀所在的那些串,我们可以对rank向前向后二分,分别求出一个以这个询问串为前缀的左端点和右端点。这样我们每次询问就是问这个左端点与右端点之间有多少个串出现,并且让这些串答案加一。

主要是有一个问题比较麻烦,就是如果这个询问串既是这个人的姓的子串,又是这个人名的子串,这时候不能算两遍。这个题不是强制在线的,于是我们可以离线下来,用一个莫队来处理。每次一个后缀加入的时候,我们判断这个后缀所在的串是否已经加入过了,然后看是否要让答案加一,对于不在区间内也是同理。对于第二问,我们的做法是,在这个串加入的时候把所有后面还没有算过的操作数加进来,当执行到让它退出这个区间的操作的时候,我们再减去这时候还没有的操作总数,那么这个差就是这一部分操作对这个串的贡献。

莫队这块要是说得不是清楚的话可以看看代码,代码还是比较清楚易懂的。

这样就做完这个题了,复杂度O(nlogn+nn)O(nlogn+n\sqrt{n})O(nlogn+nn​)。

代码:

#include <bits/stdc++.h>
using namespace std;int n,m,s[400010],rk[400010],sa[400010],he[400010][22],ji[200010],len[200010];
int cnt[400010],pos[400010],a[400010],b[400010],c[400010],ans1,num[400010],ans[400010];
struct node
{int l,r,id;
}q[400010];
inline int read()
{int x=0;char s=getchar();while(s>'9'||s<'0')s=getchar();while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}return x;
}
inline void get_sa()
{for(int i=1;i<=n;++i)rk[i]=a[i];for(int i=1;i<=n;++i)s[rk[i]]++;for(int i=1;i<=10003;++i)s[i]+=s[i-1];for(int i=n;i>=1;--i)sa[s[rk[i]]--]=i;   int p=0,ln=1,x=10003;while(p<n){int k=0;for(int i=n-ln+1;i<=n;++i)b[++k]=i;for(int i=1;i<=n;++i){if(sa[i]>ln)b[++k]=sa[i]-ln;}for(int i=1;i<=n;++i)c[i]=rk[b[i]];memset(s,0,sizeof(s));for(int i=1;i<=n;++i)s[c[i]]++;for(int i=1;i<=x;++i)s[i]+=s[i-1];for(int i=n;i>=1;--i)sa[s[c[i]]--]=b[i];     for(int i=1;i<=n;++i)c[i]=rk[i];p=1;rk[sa[1]]=1;for(int i=2;i<=n;++i){if(!(c[sa[i]]==c[sa[i-1]]&&c[sa[i]+ln]==c[sa[i-1]+ln]))++p;rk[sa[i]]=p;}x=p;ln*=2;}x=0;for(int i=1;i<=n;++i){if(rk[i]==1)continue;if(x)--x;int j=sa[rk[i]-1];while(a[i+x]==a[j+x])++x;   he[rk[i]][0]=x;}for(int j=1;(1<<j)<=n;++j){for(int i=1;i<=n-(1<<j)+1;++i)he[i][j]=min(he[i][j-1],he[i+(1<<j-1)][j-1]);      }
}
inline int lcp(int x,int y)
{int z=log2(y-x);return min(he[x+1][z],he[y-(1<<z)+1][z]);
}
inline int cmp(node x,node y)
{if(pos[x.l]!=pos[y.l])return pos[x.l]<pos[y.l];return x.r<y.r;
}
inline void ins(int x,int y)
{if(!s[c[x]]){++ans1;num[c[x]]+=m-y+1;}++s[c[x]];
}
inline void del(int x,int y)
{--s[c[x]];if(!s[c[x]]){--ans1;num[c[x]]-=m-y+1;}
}
int main()
{for(int i=1;i<=400000;++i)a[i]=1;n=read();m=read();  int qwq=0,gg=n;for(int i=1;i<=n;++i){int l=read();for(int j=1;j<=l;++j){int x=read()+1;a[++qwq]=x;}a[++qwq]=10002;l=read();for(int j=1;j<=l;++j){int x=read()+1;a[++qwq]=x;}a[++qwq]=10003;}int jilu=qwq;for(int i=1;i<=m;++i){len[i]=read();ji[i]=qwq+1;for(int j=1;j<=len[i];++j){int x=read()+1;a[++qwq]=x;}if(i!=m)a[++qwq]=10003;} n=qwq;get_sa();int sz=sqrt(n);for(int i=1;i<=n;++i)pos[i]=i/sz+1;memset(c,0,sizeof(c));int cnt=1;for(int i=1;i<=jilu;++i){if(a[i]==10003)++cnt;elsec[rk[i]]=cnt;}for(int i=1;i<=m;++i){int x=rk[ji[i]];int l=1,r=x-1,mid,res=x,jil,jir;while(l<=r){mid=(l+r)>>1;if(lcp(mid,x)>=len[i]){res=mid;r=mid-1;}elsel=mid+1;}jil=res;l=x+1;r=n;res=x;       while(l<=r){mid=(l+r)>>1;if(lcp(x,mid)>=len[i]){res=mid;l=mid+1;}elser=mid-1;}jir=res;q[i].l=jil;q[i].r=jir;q[i].id=i;}sort(q+1,q+m+1,cmp);memset(s,0,sizeof(s));int l=1,r=0;for(int i=1;i<=m;++i){while(r<q[i].r)ins(++r,i);while(r>q[i].r)del(r--,i);while(l<q[i].l)del(l++,i);while(l>q[i].l)ins(--l,i);ans[q[i].id]=ans1;if(s[0])--ans[q[i].id];}for(int i=1;i<=m;++i)printf("%d\n",ans[i]);for(int i=1;i<=gg;++i)printf("%d ",num[i]);printf("\n");return 0;
}

洛谷2336 BZOJ2754 SCOI2012 喵星球上的点名 SA 莫队 二分相关推荐

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

    Address 洛谷P2336 BZOJ2754 LOJ#2374 Solution 考虑在每个人的姓和名之间插入一个无关的字符. 这样问题就转化成了一些主串和一些模式串,询问每个模式串能匹配到多少个 ...

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

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

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

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

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

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

  5. bzoj2754: [SCOI2012]喵星球上的点名

    传送门 事实证明,我肉眼debug了两个晚上,还是不及对拍效率高. AC自动机写错了都毫不自觉的智障宸 因为数据水,写的暴力 //Achen #include<algorithm> #in ...

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

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

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

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

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

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

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

    2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 2246 Solved: 975 Description ...

最新文章

  1. 从零打造在线网盘系统之Hibernate框架起步
  2. dropbear编译安装及服务脚本编写
  3. 使用Python,机器学习和深度学习的5个很棒的计算机视觉项目创意!
  4. 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 反射获取系统的 Element[] dexElements )
  5. python如何在循环中保存文件_python-如何在for循环中更改为另一行文件
  6. centos 6.5 下 nginx 简单优化_虚拟主机_负载均衡
  7. [Python图像处理] 三十三.图像各种特效处理及原理万字详解(毛玻璃、浮雕、素描、怀旧、流年、滤镜等)
  8. python是动态语言
  9. 【转】C#+csgl库进行OpenGL编程
  10. java中子类与父类强制转换_java中父类强制转换成子类的错觉
  11. (六)Redis集群搭建
  12. unicode编码java_JAVA转化Unicode编码
  13. VCC、VDD、VSS、GND分别代表什么呢?
  14. 用VC GDI+画一颗树
  15. Linux教程shell篇——黑马课程笔记
  16. speedoffice使用方法-word怎么添加边框
  17. 「解决方案」Acrel-2000Z变电站综合自动化系统
  18. VC获取系统空闲时间
  19. Java之父求职被嫌年纪大:程序员只能吃青春饭?
  20. 下载xcode 10

热门文章

  1. leetcode【135】Candy【c++版,双数组,单数组,坡峰坡谷】
  2. 自从掌握了Google和百度的 16 个高级搜索技巧,我再也没有解决不了的 bug 了
  3. Wannafly挑战赛20-染色
  4. PHP实现数字金额轻松转成中文繁体大写
  5. 【codeforces 791A】Bear and Big Brother
  6. 【C++】黑马程序员C++核心编程学习笔记(完结)
  7. 不正经 之 15个你所须知的乳房秘密
  8. 2021年甘肃省高考成绩一分一段表查询,2021年甘肃高考成绩排名查询系统,甘肃高考位次排名查询...
  9. XiaoHu日志 6/10~6/12
  10. 查看电脑连接过的所有无线的密码