• 题解们:

    • 1.首先可以被很多暴力给搞过去;我以前也是这样水过去的
    • 2.ac自动机
      • 2.1
      • 抽离fail树
      • 对点名建自动机,建$fail$树的时候只保留询问节点;
      • 对于一个喵,子串==在自动机里匹配到的所有节点的$fail$祖先并
      • 把姓和名都放到里面去跑,得到所有的点,需要把这些点在新的$fail$树里的祖先全部标记
      • 具体按照dfs序排序,每个点$q[i]$的贡献就是$lca(q[i-1],q[i])$到$q[i]$那段
      • 统计第一问用树上差分$q[i]$处$++$,$lca(q[i-1],q[i])$处$--$,具体第二问直接记录每个点到根有多少次点名统计直接相减;
      • $O(N \ log N)$
      •  #include<bits/stdc++.h>
        #define rg register
        #define il inline
        #define Run(i,l,r) for(rg int i=l;i<=r;i++)
        #define Don(i,l,r) for(rg int i=l;i>=r;i--)
        using namespace std;
        const int N=;
        int n,m,o=,hd[N],a[N],b[N],s[N],tot,fl[N],fa[N],st[N],ed[N],idx;
        int val[N],vis[N],que[N],head,tail,sz,cnt,size[N],tp[N],dep[N],pos[N],ans[N],deep[N];
        il bool cmp(const int&x,const int&y){return st[x]<st[y];}
        map<int,int>ch[N];
        map<int,int>::iterator it;
        struct Edge{int v,nt;}E[N];
        il void adde(int u,int v){E[o]=(Edge){v,hd[u]};hd[u]=o++;}
        il char gc(){
        static char*p1,*p2,s[];
        if(p1==p2)p2=(p1=s)+fread(s,,,stdin);
        return(p1==p2)?EOF:*p1++;
        }
        il int rd(){
        int x=,f=; char c=gc();
        while(c<''||c>''){if(c=='-')f=-;c=gc();}
        while(c>=''&&c<='')x=(x<<)+(x<<)+c-'',c=gc();
        return x*f;
        }
        void get_fl(){
        for(it=ch[].begin();it!=ch[].end();it++){
        int v=it->second;
        que[++tail]=v;
        if(vis[v])adde(fa[v],v),size[v]=;
        }
        while(head < tail){
        int u=que[++head];
        for(it = ch[u].begin();it!=ch[u].end();it++){
        int v = it->second, c = it->first, w=fl[u];
        while(w&&!ch[w].count(c))w=fl[w];
        if(!ch[w].count(c))fl[v]=;
        else fl[v]=ch[w][c];
        if(vis[fl[v]])fa[v]=fl[v];
        else fa[v]=fa[fl[v]];
        que[++tail]=v;
        if(vis[v])adde(fa[v],v),size[v]=;
        }
        }
        Don(i,tail,)size[fa[que[i]]]+=size[que[i]];
        }
        void dfs(int u,int T){
        int son=;
        st[u]=++idx;tp[u]=T;
        dep[u]=dep[fa[u]]+;
        deep[u]=deep[fa[u]]+vis[u];
        for(int i=hd[u];i;i=E[i].nt){
        int v=E[i].v;
        if(!son||size[v]>size[son])son=v;
        }
        if(son)dfs(son,T);
        for(int i=hd[u];i;i=E[i].nt){
        int v=E[i].v;
        if(son!=v)dfs(v,v);
        }
        ed[u]=idx;
        }
        il int lca(int x,int y){
        int tx=tp[x],ty=tp[y];
        while(tx!=ty){
        if(dep[tx]<dep[ty])y=fa[ty],ty=tp[y];
        else x=fa[tx],tx=tp[x];
        }
        return dep[x]<dep[y]?x:y;
        }
        void find(int len){
        int x = , c;
        Run(i,,len){
        c = s[tot+i];
        while(x&&!ch[x].count(c))x=fl[x];
        if(!ch[x].count(c))x=;
        else x=ch[x][c];
        if(vis[x])que[++tail]=x;
        else if(fa[x])que[++tail]=fa[x];
        }
        tot+=len;
        }
        void dfs(int u){
        for(int i=hd[u];i;i=E[i].nt)
        dfs(E[i].v),val[u]+=val[E[i].v];
        }
        int main(){
        freopen("bzoj2754.in","r",stdin);
        freopen("bzoj2754.out","w",stdout);
        n=rd(); m=rd();
        Run(i,,n){
        a[i]=rd();Run(j,,a[i])s[++tot]=rd();
        b[i]=rd();Run(j,,b[i])s[++tot]=rd();
        }
        Run(i,,m){
        int x=rd(),u=,y;
        Run(j,,x){
        if(!ch[u][y=rd()])ch[u][y]=++sz;
        u=ch[u][y];
        }
        vis[pos[i]=u]++;
        }
        get_fl();
        dfs(,);
        tot=;
        Run(i,,n){
        tail=;
        find(a[i]),find(b[i]);
        if(!tail)continue;
        sort(que+,que+tail+,cmp);
        head=;
        Run(j,,tail){
        if(!head||ed[que[j]]>ed[que[head]])head++;
        que[head]=que[j];
        }
        tail=head;
        val[]--,val[que[]]++;
        ans[i]+=deep[que[]];
        Run(j,,tail){
        int tmp = lca(que[j-],que[j]);
        val[tmp]--,val[que[j]]++;
        ans[i] += deep[que[j]] - deep[tmp];
        }
        }
        dfs();
        Run(i,,m)printf("%d\n",val[pos[i]]);
        Run(i,,n)printf("%d ",ans[i]);
        return ;
        }

        AC

    • 3.后缀数组
      • 3.1
      • 莫队
      • 将所有串用互不相等的连接符链接,为了方便让点名串后的连接符尽量小;
      • 可以在SA里求出每次点名的区间,是后缀$i \ rank[i]$向后的一段;
      • 问题变成统计 ①一个线段里有多少种颜色的点和 ②一种颜色的点被多少条线段覆盖;
      • ①莫队模板;
      • ②因为每个区间都是不同的,考虑差分,每次从莫队的区间里加入一个颜色就加上剩余的区间数,删去就减掉,就统计了中间出现的那段区间数;
      • $O(N \ \sqrt N)$
      • 3.2
      • 树状数组
      • SA的部分一样
      • ①扫一遍,$pre[i]$表示倒着上一个扫到的和sa[i]颜色相同的位置,遇到一个每次$add(i,1)$,$add(pre[i],-1)$,直接统计对应区间;
      • ②扫一遍,对于区间$[L,R]$,在$R$的位置$add(R,1)$ ,$L-1$的位置$add(L,-1)$ ,统计$i$到$pre[i]-1$的数量;
      • 均可树状数组维护
      • $O(N \ log N)$
      •  #include<bits/stdc++.h>
        #define il inline
        #define rg register
        using namespace std;
        const int N=,M=;
        int n,m,len,cnt,s[N],sub[N],tot,sa[N],ht[N],rk[N],pre[N],bl[N],pos[N],f[N][M],bin[M],l[N],mp[N],c[N],ans1[N],ans2[N];
        struct data{
        int x,y,z;
        bool operator <(const data&A)const{return x < A.x;};
        }p[N];
        il char gc(){
        static char*p1,*p2,s[];
        if(p1==p2)p2=(p1=s)+fread(s,,,stdin);
        return(p1==p2)?EOF:*p1++;
        }
        il int rd(){
        int x=,f=; char ch=gc();
        while(ch<''||ch>''){if(ch=='-')f=-;ch=gc();}
        while(ch>=''&&ch<='')x=(x<<)+(x<<)+ch-'',ch=gc();
        return x*f;
        }
        il void add(int x,int y){for(rg int i=x+;i<=len;i+=i&-i)c[i]+=y;}
        il int que(int x){int re=;for(rg int i=x+;i;i-=i&-i)re+=c[i];return re;}
        void discretize(){
        sort(sub,sub+tot);
        tot=unique(sub,sub+tot)-sub;
        for(rg int i=;i<len;i++)s[i]=lower_bound(sub,sub+tot,s[i])-sub;
        }
        void build_sa(){
        static int x[N],y[N],w[N];
        for(rg int i=;i<len;i++)w[x[i]=s[i]]++;
        for(rg int i=;i<tot;i++)w[i]+=w[i-];
        for(rg int i=len-;~i;i--)sa[--w[x[i]]]=i;
        for(rg int k=;k<len;k<<=){
        int p = ;
        for(rg int i=len-k;i<len;i++)y[p++]=i;
        for(rg int i=;i<len;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
        for(rg int i=;i<tot;i++)w[i]=;
        for(rg int i=;i<len;i++)w[x[i]]++;
        for(rg int i=;i<tot;i++)w[i]+=w[i-];
        for(rg int i=len-;~i;i--)sa[--w[x[y[i]]]]=y[i];
        swap(x,y);
        x[sa[]]=; p=;
        for(rg int i=;i<len;i++){
        x[sa[i]] = y[sa[i]]==y[sa[i-]]&&y[sa[i]+k]==y[sa[i-]+k] ? p - : p++;
        }
        if(p==len)break;
        tot = p + ;
        }
        }
        void build_ht(){
        for(rg int i=;i<len;i++)rk[sa[i]]=i;
        for(rg int i=,k=,j;i<len;i++){
        if(k)k--;
        j=sa[rk[i]-];
        while(s[j+k]==s[i+k])k++;
        f[rk[i]][]=ht[rk[i]]=k;
        }
        }
        void build_rmq(){
        for(rg int i=;i<;i++)
        for(rg int j=;j<=len-bin[i];j++){
        f[j][i] = min(f[j][i-],f[j+bin[i-]][i-]);
        }
        }
        int main(){
        freopen("bzoj2754.in","r",stdin);
        freopen("bzoj2754.out","w",stdout);
        for(int i=bin[]=;i<;i++)bin[i]=bin[i-]<<;
        n=rd(); m=rd();
        for(rg int i=,x;i<=n;i++){
        x=rd();
        for(rg int j=;j<=x;j++){
        bl[len]=i;
        sub[tot++]=s[len++]=rd();
        }sub[tot++]=s[len]=-len,len++;
        x=rd();
        for(rg int j=;j<=x;j++){
        bl[len]=i;
        sub[tot++]=s[len++]=rd();
        }sub[tot++]=s[len]=-len,len++;
        }
        for(rg int i=,x;i<=m;i++){
        l[i]=x=rd();
        bl[len]=-i;
        for(rg int j=;j<=x;j++){
        sub[tot++]=s[len++]=rd();
        }sub[tot++]=s[len]=-len,len++;
        }
        discretize();
        build_sa();
        build_ht();
        build_rmq();
        for(rg int i=len-;~i;i--)if(bl[sa[i]]>){
        int x = bl[sa[i]];
        if(!mp[x])add(mp[x]=i,);
        else{
        pre[i]=mp[x],mp[x]=i;
        add(pre[i],-),add(i,);
        }
        }else if(bl[sa[i]]<){
        int x=-bl[sa[i]],y=i+;
        for(rg int j=;~j;j--)if(f[y][j]>=l[x])y+=bin[j];
        p[++cnt]=(data){y-,y-,};
        p[++cnt]=(data){i-,y-,-};
        ans1[x] = que(y-) - que(i-);
        }
        memset(c,,sizeof(c));
        sort(p+,p+cnt+);
        for(rg int i=len-,j=cnt;~i;i--){
        while(j&&p[j].x==i)add(p[j].y,p[j].z),j--;
        if(bl[sa[i]]>){
        if(!pre[i])pre[i]=len;
        ans2[bl[sa[i]]] += que(pre[i]-) - que(i-);
        }
        }
        for(rg int i=;i<=m;i++)printf("%d\n",ans1[i]);
        for(rg int i=;i<=n;i++)printf("%d ",ans2[i]);
        return ;
        }

        SA+BIT

    • 4.后缀自动机
      • 4.1
      • 广义后缀自动机
      • 至少这题和$SAM$差不多,只是新加一个单词重置$last$节点;
      • 对点名串建出$SAM$之后,把姓名串在上面跑,对走过的点沿$parent$树向上跳可以找到所有子串,标记是否来过暴力统计;
      • 这样两个问的方法是一样的
      • $O(N \ \sqrt N )$
      • 4.2
      • 但其实如果建出$parent$树的话就和$1$一样,如果对$parent$树做$dfs$序维护的话就和$2$一样了,不说了;
      •  #include<bits/stdc++.h>
        #define rg register
        #define il inline
        using namespace std;
        const int N=;
        int n,m,s[N],tot,a[N],b[N],lst,cnt,pa[N],len[N],sum[N],val[N],vis[N],ans;
        map<int,int>ch[N];
        il char gc(){
        static char*p1,*p2,s[];
        if(p1==p2)p2=(p1=s)+fread(s,,,stdin);
        return(p1==p2)?EOF:*p1++;
        }
        il int rd(){
        int x=,f=; char c=gc();
        while(c<''||c>''){if(c=='-')f=-;c=gc();}
        while(c>=''&&c<='')x=(x<<)+(x<<)+c-'',c=gc();
        return x*f;
        }
        il void ins(int x){
        int p=lst,np; len[np=lst=++cnt]=len[p]+;
        while(p&&!ch[p][x])ch[p][x]=np,p=pa[p];
        if(!p){pa[np]=;return;}
        int q = ch[p][x];
        if(len[q]==len[p]+)pa[np]=q;
        else{
        int nq=++cnt;
        len[nq]=len[p]+;
        ch[nq]=ch[q];
        pa[nq]=pa[q]; pa[q]=pa[np]=nq;
        while(p&&ch[p][x]==q)ch[p][x]=nq,p=pa[p];
        }
        }
        il void update(int x,int y){while(x&&vis[x]!=y)sum[x]++,vis[x]=y,x=pa[x];}
        il void query(int x,int y){while(x&&vis[x]!=y)ans+=val[x],vis[x]=y,x=pa[x];}
        int main(){
        freopen("lg2336.in", "r", stdin);
        freopen("lg2336.out","w",stdout);
        n=rd();m=rd();
        cnt=;
        for(rg int i=;i<=n;i++){
        lst=;a[i]=rd();
        for(rg int j=;j<=a[i];j++)ins(s[++tot]=rd());
        lst=;b[i]=rd();
        for(rg int j=;j<=b[i];j++)ins(s[++tot]=rd());
        }
        tot=;
        for(rg int i=;i<=n;i++){
        for(rg int j=,now=;j<=a[i];j++)update(now=ch[now][s[++tot]],i);
        for(rg int j=,now=;j<=b[i];j++)update(now=ch[now][s[++tot]],i);
        }
        for(rg int i=,l,now;i<=m;i++){
        l=rd();
        now=;
        for(rg int j=,x;j<=l;j++){
        x=rd();
        if(!now)continue;
        if(!ch[now].count(x))now=;
        else now=ch[now][x];
        }
        if(now)val[now]++;
        printf("%d\n",sum[now]);
        }
        tot=;
        for(rg int i=;i<=n;i++){
        ans=;
        for(rg int j=,now=;j<=a[i];j++)query(now=ch[now][s[++tot]],n+i);
        for(rg int j=,now=;j<=b[i];j++)query(now=ch[now][s[++tot]],n+i);
        printf("%d ",ans);
        }
        return ;
        }

        SAM

    • 就数据来看,最快的应该是$3.1$(我没写QAQ),再来就是$2.1,4.1,3.2$,,不算$map$的话$tarjan$写lca,理论最好的应该是$2.1$ ;

【bzoj2754】【scoi2012】喵星球上的点名的更多相关文章

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

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

  2. [BZOJ2754] [SCOI2012]喵星球上的点名解题报告|后缀数组

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

  3. bzoj2754:[SCOI2012]喵星球上的点名(后缀自动机)

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

  4. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机)

    Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 2816  Solved: 1246[Submit][Status][Discuss] Descript ...

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

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

  6. BZOJ2754 SCOI2012喵星球上的点名

    绝世好题. 正当我犹豫不决时,hzwer说:“MAP!!!” 没错这题大大的暴力,生猛的stl,贼基尔爽,,ԾㅂԾ,, 由于我们求点名在名字中的子串个数,所以将点名建AC自动机,记录节点属于哪次点名, ...

  7. BZOJ2754 [SCOI2012]喵星球上的点名 SA+莫队+树状数组

    题面 戳这里 题解 首先先把所有给出的姓名和询问全部接在一起,建出\(height\)数组. 某个串要包含整个询问串,其实就相当于某个串与询问串的\(lcp\)为询问串的长度. 而两个后缀\(Suff ...

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

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

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

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

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

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

随机推荐

  1. GlusterFS分布式存储集群-2. 使用

    参考文档: Quick Start Guide:http://gluster.readthedocs.io/en/latest/Quick-Start-Guide/Quickstart/ Instal ...

  2. ubuntu 相关软件设置

    软件篇 1. 网易云音乐软件 首先去官网下载网易云音乐客户端linux版,网址:http://music.163.com/#/download,选择linux版本,然后选择ubuntu 16.04(6 ...

  3. Ruby知识点三:运算符

    1.逻辑运算符 (1)条件1 || 条件2 条件1为假时,才需判断条件2 (2)条件1 && 条件2 条件1为真时,才需判断条件2 2.范围运算符 (1)x..y  从x到y,包括y ...

  4. which命令详解

    基础命令学习目录首页 原文链接:https://www.cnblogs.com/jkin/p/10289085.html Linux which命令用于查找文件. which指令会在环境变量$PATH ...

  5. NodeJS http模块

    Node.js提供了http模块,用于搭建HTTP服务端和客户端. 创建Web服务器 /** * node-http 服务端 */ let http = require('http'); let ur ...

  6. python循环综合运用

    循环很重要,计算机很蠢,唯一的优势就是按照指令不停的执行,所以决定在说一下. break语句,用在循环体中,迫使循环立即终止,即跳出所在循环体,继续执行循环体后面的语句. sum=0 i=1 whil ...

  7. Vim YouCompleteMe 安装配置

    YouCompleteMe是很强大的vim插件,可以提供强大的补齐功能,曾经多次尝试安装,都没有配置成功,最近在一个契机下,看到有同事的配置,自己在边尝试和边咨询后,终于也搞定了,遂记录下. 官网有最 ...

  8. sprint2(第九天)

    今天是sprint2的最后一天,已经完成功能有可以实现点餐功能.菜品的添加和删减.菜品数量的增减.添加备注.查看订单详情.订单状态.提交订单.后厨可以查看订单信息,对菜品的状态进行操作,是否完成烹饪, ...

  9. HDU 3811 Permutation 状压dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3811 Permutation Time Limit: 6000/3000 MS (Java/Othe ...

  10. 【CSAPP笔记】6. 汇编语言——控制

    原先刊于自己的域名下面,考虑到博客园之前发过一半,不想烂尾,故在博客园发一版. 到目前为止我们只考虑了直线代码的执行行为,也就是指令一条接着一条执行.C语言中的某些语句,比如条件语句.循环.分支语句, ...