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\n",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\n",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\n",ans1[i]); for(int i=1;i<=n;++i)printf("%d%c",ans2[i],(i==n)?'\n':' '); return 0; }