zoukankan      html  css  js  c++  java
  • bzoj2754[SCOI2012]喵星球上的点名

    这道题的输出格式好萌好喵啊,竟然是竖着一列然后横着再拐过去的。

    题意:给出n组字符串,每组两个字符串A和B(“姓”和“名”),给出m个询问,每个询问包含一个串C,对于某一组字符串,如果C是A或者B的子串,那么称C在这组字符串中出现.对于每个询问串,输出它在多少组字符串中出现.对于每组字符串,输出有多少个询问串在这一组字符串中出现.n<=20000,m<=50000,询问串总长<=100000,n组字符串总长<=100000,字符集大小10000,

    分析:这个题看上去很像模式匹配问题,又是多串,所以考虑AC自动机.

    先处理每个询问串在多少组中出现.先考虑AC自动机可以处理的较简单的问题.

    如果对每个询问串只统计总共出现了多少次(在同一个串中多次出现算作多次,没有“一组”的限制),AC自动机可以直接做.

    如果统计每个询问串在多少个串中出现,(在同一个串中多次出现算作一次但不考虑一组的限制,在同一组的两个串中出现算作两次)(也就是bzoj2780 sevenk loves oimaster或bzoj3881Divljak),AC自动机+树链的并,利用树状数组和fail树的DFS序可以做.

    现在如果某个询问串在一组中的两个串里都出现了,应当只算一次,那么我们把这一组的两个串在AC自动机上经过的节点一起做树链的并即可.建议先做bzoj2780和bzoj3881.这道题的操作并不是在线的,应该可以使用树上差分打打标记,不过树状数组也很好写并且复杂度足够.

    第二问的处理方法和第一问相同,也是树链的并,我们预处理fail树上每个节点x到根节点路径上单词节点的个数sum[x],然后用树链的并求和:首先将一组的两个串扔进AC自动机跑,把经过的节点拿出来按fail树的dfs序排序,首先把每个经过的节点的sum值加起来,这时的答案是有重复计数的,然后对dfs序相邻的两个节点,把答案减去它们的lca的sum值.

    这两问的处理方法其实具有对称性,一个进行求和,一个进行修改.

    因为字符集过大,AC自动机上节点的儿子数组需要用map,当然也不能补全为trie图.AC自动机匹配的时候会需要沿着fail指针往回跳,但复杂度还是线性的.(我一开始忘了,以为不补成trie图的复杂度是错的....)

    粘一些链接:

    对解法的一些总结http://www.tuicool.com/articles/zYrmM3Z 

    这个小哥写了树链的并,然后又写HH的项链,并不是很懂.http://blog.csdn.net/clover_hxy/article/details/52502544 

    远古神犇Vani的神代码https://github.com/Azure-Vani/acm-icpc/blob/master/bzoj/2754.cpp

    #include<cstdio>
    #include<cassert>
    #include<algorithm>
    #include<queue>
    #include<map>
    using namespace std;
    const int maxn=50005;
    struct node{
      map<int,node*> ch;
      node* fail;int num,val;
      node(int x){
        ch.clear();fail=0;num=x;val=0;
      }
    }*root;int tot=1;
    int str[maxn];
    int len1[maxn],len2[maxn],lenq[maxn];
    vector<int> name1[maxn],name2[maxn],query[maxn];
    node* pos[100005];int pos2[100005];
    void Add(vector<int> str,int len,int x){
      node* p=root;
      for(int i=0;i<len;++i){
        if(p->ch[str[i]]==NULL){
          p->ch[str[i]]=new node(++tot);pos[tot]=p->ch[str[i]];
        }
        p=p->ch[str[i]];
      }
      p->val++;pos2[x]=p->num;
    }
    struct edge{
      int to,next;
    }lst[100005];int len=1,first[100005];
    void addedge(int a,int b){//printf("%d %d
    ",a,b);
      lst[len].to=b;lst[len].next=first[a];first[a]=len++;
    }
    void getfail(){
      queue<node*> q;q.push(root);
      while(!q.empty()){
        node* x=q.front();q.pop();
        for(map<int,node*>::iterator pt=x->ch.begin();pt!=x->ch.end();++pt){
          if(x==root)pt->second->fail=root;
          else{
        node* p=x->fail;int t=pt->first;
        while(p!=root&&p->ch[t]==NULL)p=p->fail;
        if(p->ch[t])pt->second->fail=p->ch[t];
        else pt->second->fail=root;
          }
          addedge(pt->second->fail->num,pt->second->num);
          q.push(pt->second);
        }
      }
    }
    int ans1[maxn],ans2[maxn];
    int seq[100005],cnt=0;
    int dfn[100005],T,p[100005][20],depth[100005],sum[100005],sz[100005];
    void dfs(int x){
      dfn[x]=++T;
      for(int j=0;p[x][j];++j)p[x][j+1]=p[p[x][j]][j];
      sz[x]=1;
      for(int pt=first[x];pt;pt=lst[pt].next){
        p[lst[pt].to][0]=x;depth[lst[pt].to]=depth[x]+1;
        sum[lst[pt].to]=sum[x]+pos[lst[pt].to]->val;
        dfs(lst[pt].to);sz[x]+=sz[lst[pt].to];
      }
    }
    int lca(int u,int v){
      if(depth[u]<depth[v])swap(u,v);
      for(int j=19;j>=0;--j){
        if(depth[p[u][j]]>=depth[v])u=p[u][j];
      }
      if(u==v)return u;
      for(int j=19;j>=0;--j)if(p[u][j]!=p[v][j])u=p[u][j],v=p[v][j];
      return p[v][0];
    }
    void go(vector<int> str,int n){
      node* p=root;
      for(int i=0;i<n;++i){
        int t=str[i];
        while(p!=root&&p->ch[t]==NULL)p=p->fail;
        if(p->ch[t])p=p->ch[t];
        if(p!=root)seq[cnt++]=p->num;
      }
    }
    bool cmp(const int &a,const int &b){
      return dfn[a]<dfn[b];
    }
    int bit[100005];
    void add(int x,int w){
      for(;x<100005;x+=x&(-x))bit[x]+=w;
    }
    int pre(int x){
      int ans=0;for(;x;x-=x&(-x))ans+=bit[x];return ans;
    }
    int calc(int l,int r){
      return pre(r)-pre(l-1);
    }
    void process(int x){
      cnt=0;
      go(name1[x],len1[x]);go(name2[x],len2[x]);//printf("%d
    ",cnt);
      sort(seq,seq+cnt,cmp);
      for(int i=0;i<cnt;++i){
        add(dfn[seq[i]],1);ans2[x]+=sum[seq[i]];
      }
      int LCA;
      for(int i=1;i<cnt;++i){
        LCA=lca(seq[i],seq[i-1]);
        add(dfn[LCA],-1);ans2[x]-=sum[LCA];
      }
    }
    int main(){
      int n,m,x;scanf("%d%d",&n,&m);
      root=new node(1);pos[1]=root;
      for(int i=1;i<=n;++i){
        scanf("%d",&len1[i]);
        for(int j=0;j<len1[i];++j){
          scanf("%d",&x);name1[i].push_back(x);
        }
        scanf("%d",&len2[i]);
        for(int j=0;j<len2[i];++j){
          scanf("%d",&x);name2[i].push_back(x);
        }
      }
      for(int i=1;i<=m;++i){
        scanf("%d",&lenq[i]);
        for(int j=0;j<lenq[i];++j){
          scanf("%d",&x);query[i].push_back(x);
        }
        Add(query[i],lenq[i],i);
      }
      getfail();
      dfs(1);
      for(int i=1;i<=n;++i){
        process(i);
      }
      for(int i=1;i<=m;++i){
        int x=pos2[i];
        ans1[i]=calc(dfn[x],dfn[x]+sz[x]-1);
      }
      for(int i=1;i<=m;++i)printf("%d
    ",ans1[i]);
      for(int i=1;i<=n;++i)printf("%d%c",ans2[i],(i==n)?'
    ':' ');
      return 0;
    }
  • 相关阅读:
    用网线连接Windows和Linux台式机,并实现Linux共享Windows的WiFi网络
    设计模式之建造者设计模式
    Mob之社会化分享集成ShareSDK
    Mob 之 短信验证集成 SMSSDK
    天地图值之添加覆盖物
    天地图之定位信息详解
    Material Design 组件之NavigationView
    Material Design 组件之 CollapsingToolbarLayout
    Material Design 组件之 AppBarLayout
    Material Design 组件之 FloatingActionButton
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6550071.html
Copyright © 2011-2022 走看看