• 题解们:

    • 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. Kubernetes网络方案 Flannel和calico

    摘抄某博客 1.   Flannel Flannel是为kubernetes设计的一个非常简洁的多节点三层网络方案,解决不同host上的容器互联问题,原理是为每个host分配一个subnet,容器从此 ...

  2. 最新Microsoft Edge!使用chromium内核

    2018年11月,微软宣布其Edge浏览器将采用Chromium引擎,意味着微软的Edge浏览器以失败告终. 但令人振奋的是,新版Edge也许会“死而复生”.在使用了Chromium内核后,Edge各 ...

  3. IOS git 删除仓库 新建仓库 提交 合并 操作 码云

    HDHaoShaoPengdeiMac:~ hdhaoshaopeng$ defaults write com.apple.finder AppleShowAllFiles TRUE HDHaoSha ...

  4. “秒杀”问题的数据库和SQL设计【转载】

    “秒杀”问题的数据库和SQL设计 APRIL 21ST, 2015 问题的来源 完全不考虑一致性的方案 表结构 方案 存在的问题 保证单用户不会重复购买 解决超卖问题 方案 优化 提高性能了 鱼与熊掌 ...

  5. Scrum Meeting 报告

    Scrum Meeting 报告 ----团队项目所需时间估计以及任务分配 由于能力有限,我们还不能构架好一个大框架.但是初步可以完成任务的流程和分配.任务所需要的具体实现可以参看<学霸系统的N ...

  6. 作业MathExam

    MathExam233 一.预估与实际 PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟) Planning 计划 600 650 • ...

  7. Scapy 网段中ping扫描

    安装scapy pip3 install scapy-python3 交互式ip包构造 #scapy >>> ping = sr(IP(dst='202.100.1.1')/ICMP ...

  8. Python中,os.listdir遍历纯数字文件乱序如何解决

    Python中,os.listdir遍历纯数字文件乱序如何解决 日常跑深度学习视觉相关代码时,常常需要对数据集进行处理.许多图像文件名是利用纯数字递增的方式命名.通常所用的排序函数sort(),是按照 ...

  9. windows redis 后台运行

    1. 进入 DOS窗口2. 在进入Redis的安装目录3. 输入:redis-server --service-install redis.windows.conf --loglevel verbos ...

  10. vs2010调试-尝试调试dll源码。

    第一步: 打开“调试”——“选项和设置”——点击调试下“常规”——设置启用“启用.NET Framework源代码单步执行 ” 第二步 选择“符号”——选择Microsoft符号服务器——设置符号缓存 ...