题目描述
a180285 幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。
假设课堂上有 N 个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M 个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。
然而,由于喵星人的字码过于古怪,以至于不能用 ASCII 码来表示。为了方便描述,a180285 决定用数串来表示喵星人的名字。
现在你能帮助 a180285 统计每次点名的时候有多少喵星人答到,以及 M 次点名结束后每个喵星人答到多少次吗?
输入输出格式
输入格式:
现在定义喵星球上的字符串给定方法:
先给出一个正整数 L ,表示字符串的长度,接下来L个整数表示字符串的每个字符。
输入的第一行是两个整数 N 和 M。
接下来有 N 行, 每行包含第 i 个喵星人的姓和名两个串。 姓和名都是标准的喵星球上的字符串。
接下来有 M 行,每行包含一个喵星球上的字符串,表示老师点名的串。
输出格式:
对于每个老师点名的串输出有多少个喵星人应该答到。
然后在最后一行输出每个喵星人被点到多少次。
~~~~~~~~~~~~~~~~~~
某谷恶意加强数据。
首先吐槽卡trie图。字符集太大了,trie图会T得很厉害,建个图就超时了。
别人用map,然而我不喜欢迭代器,就用了treap维护儿子。
代码:
#include<ctime> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define N 200050 #define M 200050 inline int rd() { int f=1,c=0;char ch = getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=(c<<3)+(c<<1)+ch-'0';ch=getchar();} return f*c; } int n,m,tot; int ls[M<<1],rs[M<<1],rnd[M<<1],w[M<<1]; struct Treap { int rt; void lturn(int &x) { int y = rs[x]; rs[x]=ls[y],ls[y]=x; x=y; } void rturn(int &x) { int y = ls[x]; ls[x]=rs[y],rs[y]=x; x=y; } int query(int k,int x) { if(w[k]==x)return k; if(!k)return 0; return query(w[k]<x?rs[k]:ls[k],x); } void insert(int &k,int x) { if(!k) { k=++tot; rnd[k]=rand(); w[k]=x; return ; }else if(w[k]<x) { insert(rs[k],x); if(rnd[rs[k]]<rnd[k])lturn(k); }else { insert(ls[k],x); if(rnd[ls[k]]<rnd[k])rturn(k); } } }; int lin[M],sum; struct nam { int len1,len2; int l1,r1,l2,r2; }q[N]; int hed[M<<1],cntt; struct Trie { Treap ch; // vector<int>vv; int f,ct; }tr[M<<1]; struct EG { int to,nxt; }e[M]; void ae(int f,int t) { e[++cntt].to = t; e[cntt].nxt = hed[f]; hed[f] = cntt; } bool vis1[M],vis2[M]; void init() { for(int i=1;i<=tot;i++) vis1[i]=0; for(int i=1;i<=n;i++) vis2[i]=0; } void acmach() { queue<int>q,c; q.push(0); while(!q.empty()) { int u = q.front(); q.pop(); if(!tr[u].ch.rt)continue; c.push(tr[u].ch.rt); while(!c.empty()) { int v = c.front(); c.pop(); if(ls[v])c.push(ls[v]); if(rs[v])c.push(rs[v]); int to = tr[u].f; while(to&&!tr[to].ch.query(tr[to].ch.rt,w[v]))to=tr[to].f; int hhh=tr[to].ch.query(tr[to].ch.rt,w[v]); if(hhh&&u)to=hhh; tr[v].f = to; q.push(v); } } } int as1[N],as2[M]; int deal(int u) { int ret = 0; while(!vis1[u]) { for(int j=hed[u];j;j=e[j].nxt) { int i = e[j].to; vis2[i]=1; as2[i]++; } ret+=tr[u].ct; vis1[u]=1; u = tr[u].f; } return ret; } int main() { srand(time(NULL)); n=rd(),m=rd(); int u,c,v; for(int i=1;i<=n;i++) { q[i].len1=rd(); q[i].l1 = sum+1,q[i].r1 = sum+q[i].len1; for(int j=q[i].l1;j<=q[i].r1;j++)lin[j]=rd(); sum+=q[i].len1; q[i].len2=rd(); q[i].l2 = sum+1,q[i].r2 = sum+q[i].len2; for(int j=q[i].l2;j<=q[i].r2;j++)lin[j]=rd(); sum+=q[i].len2; } for(int i=1;i<=m;i++) { c=rd(); u=0; for(int j=1;j<=c;j++) { v=rd(); if(!tr[u].ch.query(tr[u].ch.rt,v))tr[u].ch.insert(tr[u].ch.rt,v); u = tr[u].ch.query(tr[u].ch.rt,v); } // tr[u].vv.push_back(i); ae(u,i); tr[u].ct++; } acmach(); vis1[0]=1; for(int i=1;i<=n;i++) { init(); u=0; for(int j=q[i].l1;j<=q[i].r1;j++) { c = lin[j]; while(u&&!tr[u].ch.query(tr[u].ch.rt,c))u=tr[u].f; int hhh = tr[u].ch.query(tr[u].ch.rt,c); if(hhh)u=hhh; as1[i]+=deal(u); } u=0; for(int j=q[i].l2;j<=q[i].r2;j++) { c = lin[j]; while(u&&!tr[u].ch.query(tr[u].ch.rt,c))u=tr[u].f; int hhh = tr[u].ch.query(tr[u].ch.rt,c); if(hhh)u=hhh; as1[i]+=deal(u); } } for(int i=1;i<=m;i++)printf("%d ",as2[i]); for(int i=1;i<=n;i++)printf("%d ",as1[i]); printf(" "); return 0; }
后来才知道AC自动机并不是正解,只是当时数据水能过……
现在讲一下后缀自动机做法(貌似也不是正解)
对于姓名串我们用广义后缀自动机存上,然后离线处理点名达到数。
处理方法依然为树状数组之HH的项链。
这一段复杂度为O(n logn *map的log);
对于答道次数,我们可以将姓名串放在后缀自动机上跑一遍,每到一个节点就沿着pre指针跳到第一个到过的地方。
复杂度好像O(n)?
代码:
#include<map> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 50050 #define M 100050 int n,m; struct Point { int pre,len; map<int,int>trs; vector<int>ve; }p[2*M]; struct EG { int to,nxt; }e[2*M]; int hed[2*M],cnt; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } int tin[2*M],tout[2*M],tim,pla[2*M],v[2*M]; struct SAM { int tot,las; SAM(){tot=las=1;} void res(){las=1;} void insert(int c,int k) { int np,nq,lp,lq; np=++tot; p[np].len = p[las].len+1; for(lp = las;lp&&p[lp].trs.find(c)==p[lp].trs.end();lp=p[lp].pre) p[lp].trs[c] = np; if(!lp)p[np].pre = 1; else { lq = p[lp].trs[c]; if(p[lq].len==p[lp].len+1)p[np].pre = lq; else { nq=++tot; p[nq] = p[lq]; p[nq].len = p[lp].len+1; p[lq].pre = p[np].pre = nq; while(p[lp].trs[c]==lq) { p[lp].trs[c]=nq; lp = p[lp].pre; } } } las = np; p[np].ve.push_back(k); } void dfs(int u) { tin[u]=++tim;pla[tim]=u; for(int j=hed[u];j;j=e[j].nxt)dfs(e[j].to); tout[u]=tim; } void build() { for(int i=2;i<=tot;i++) ae(p[i].pre,i); dfs(1); } }sam; vector<int>nam[N][2]; int ct,ans[M]; int las[N]; struct Que { int l,r,id; }que[M]; bool cmp(Que a,Que b) { return a.r<b.r; } int f[2*M]; void up(int x,int d) { if(!x)return ; while(x<=200000)f[x]+=d,x+=(x&(-x)); } int down(int x) { if(!x)return 0; int ret = 0; while(x)ret+=f[x],x-=(x&(-x)); return ret; } int vis[2*M]; int main() { scanf("%d%d",&n,&m); for(int c,x,i=1;i<=n;i++) { scanf("%d",&x); sam.res(); while(x--) { scanf("%d",&c); nam[i][0].push_back(c); sam.insert(c,i); } scanf("%d",&x); sam.res(); while(x--) { scanf("%d",&c); nam[i][1].push_back(c); sam.insert(c,i); } } sam.build(); for(int c,x,i=1;i<=m;i++) { scanf("%d",&x); int u=1; while(x--) { scanf("%d",&c); if(p[u].trs.find(c)==p[u].trs.end()){u=0;} u=p[u].trs[c]; } if(u) { ct++; que[ct].l = tin[u]; que[ct].r = tout[u]; que[ct].id = i; v[u]++; } } sort(que+1,que+1+ct,cmp); for(int k=1,i=1;i<=tim;i++) { for(int j=0;j<p[pla[i]].ve.size();j++) { up(i,1); up(las[p[pla[i]].ve[j]],-1); las[p[pla[i]].ve[j]]=i; } int tmp = down(i); for(;que[k].r==i;k++) ans[que[k].id] = tmp - down(que[k].l-1); } for(int i=1;i<=m;i++) printf("%d ",ans[i]); for(int i=1;i<=n;i++) { int c = 0,u=1; for(int j=0;j<nam[i][0].size();j++) { u = p[u].trs[nam[i][0][j]]; int tmp = u; while(tmp!=1&&vis[tmp]<i) { vis[tmp]=i; c+=v[tmp]; tmp=p[tmp].pre; } } u=1; for(int j=0;j<nam[i][1].size();j++) { u = p[u].trs[nam[i][1][j]]; int tmp = u; while(tmp!=1&&vis[tmp]<i) { vis[tmp]=i; c+=v[tmp]; tmp=p[tmp].pre; } } printf("%d ",c); } printf(" "); return 0; }