对于点名串建AC自动机。。数字太大所以写了棵treap>_<......
然后每个人直接暴力在AC自动机上跑名字就行了。每次匹配到一个节点都要沿着fail链更新答案。
大概显然这样会被随便卡。所以有个小优化就是,沿着fail跳的时候如果之前已经走过了就不要再走了。具体实现用int比bool好一些。(懒得初始化系列)
一开始#2。。。然后写了个输出优化,再把不必要的一些东西改掉后就#1了2333
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 using namespace std; 6 const int maxn=100233; 7 struct zs{ 8 int too,pre; 9 }; 10 int lc[maxn],rc[maxn],rnd[maxn],num[maxn],pre[maxn],tot;//pre[i]节点i最近被谁钦点 11 int rt[maxn],s[maxn],fail[maxn],size[maxn]; 12 int dl[maxn],DL[maxn]; 13 int len[2][maxn]; 14 15 int namlast[maxn],namtot;zs name[maxn]; 16 int surlast[maxn],surtot;zs sure[maxn]; 17 int vislast[maxn],vistot;zs vise[maxn];//变量名正确读法>_<:vis_last,vis_tot,vis_e 18 int called[20023],call[maxn],endpos[50023]; 19 int i,j,n,m,k; 20 21 22 inline void lturn(int &x,int R){ 23 rc[x]=lc[R],lc[R]=x,x=R; 24 } 25 inline void rturn(int &x,int L){ 26 lc[x]=rc[L],rc[L]=x,x=L; 27 } 28 inline void ins(int &x,int v){ 29 if(!x){x=++tot,rnd[x]=rand()+(x<<1),num[x]=v;return;} 30 if(v<num[x]){ 31 ins(lc[x],v); 32 if(rnd[lc[x]]<rnd[x])rturn(x,lc[x]); 33 }else if(v>num[x]){ 34 ins(rc[x],v); 35 if(rnd[rc[x]]<rnd[x])lturn(x,rc[x]); 36 }else return; 37 } 38 void getch(int x,int v,int &son){ 39 while(x&&num[x]!=v) x=v<num[x]?lc[x]:rc[x]; 40 son=x; 41 } 42 inline void insert(int len,int id){ 43 register int i,now=0,tmp; 44 for(i=1;i<=len;i++){ 45 getch(rt[now],s[i],tmp); 46 if(tmp)now=tmp; 47 else ins(rt[now],s[i]),now=tot; 48 } 49 size[endpos[id]=now]++; 50 } 51 inline void getfail(){ 52 register int i,now,tmp,l=0,r=1,L,R,NOW;dl[1]=0; 53 while(l<r){ 54 now=dl[++l]; 55 if(rt[now])dl[++r]=rt[now]; 56 for(L=0,R=rt[now]!=0,DL[R]=rt[now];L<R;){ 57 NOW=DL[++L]; 58 if(!now)fail[NOW]=0;else{ 59 i=fail[now],getch(rt[i],num[NOW],tmp); 60 while(i&&!tmp)i=fail[i],getch(rt[i],num[NOW],tmp); 61 fail[NOW]=tmp; 62 } 63 if(lc[NOW])DL[++R]=dl[++r]=lc[NOW]; 64 if(rc[NOW])DL[++R]=dl[++r]=rc[NOW]; 65 } 66 } 67 } 68 69 70 int ra;char rx; 71 inline int read(){ 72 rx=getchar(),ra=0; 73 while(rx<'0'||rx>'9')rx=getchar(); 74 while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra; 75 } 76 char ss[12];int llen; 77 inline void outx(int x){ 78 if(!x){putchar('0');return;} 79 while(x)ss[++llen]=x%10,x/=10; 80 while(llen)putchar(ss[llen--]+48); 81 } 82 83 84 int main(){ 85 register int i,j,len,k,now,to; 86 n=read(),m=read(); 87 for(i=1;i<=n;i++){ 88 int len=read(); 89 for(j=1;j<=len;j++)name[++namtot].too=read(),name[namtot].pre=namlast[i],namlast[i]=namtot; 90 len=read(); 91 for(j=1;j<=len;j++)sure[++surtot].too=read(),sure[surtot].pre=surlast[i],surlast[i]=surtot; 92 } 93 for(i=1;i<=m;insert(j,i),i++) 94 for(len=j=read();len;len--)s[len]=read(); 95 getfail(); 96 for(i=1;i<=n;i++){ 97 for(j=namlast[i],now=0;j;j=name[j].pre){ 98 for(getch(rt[now],name[j].too,to);now&&!to;now=fail[now],getch(rt[now],name[j].too,to)); 99 for(k=now=to;k&&pre[k]<i;pre[k]=i,k=fail[k])if(size[k]) 100 called[i]+=size[k],call[k]++; 101 } 102 103 for(j=surlast[i],now=0;j;j=sure[j].pre){ 104 for(getch(rt[now],sure[j].too,to);now&&!to;now=fail[now],getch(rt[now],sure[j].too,to)); 105 for(k=now=to;k&&pre[k]<i;pre[k]=i,k=fail[k])if(size[k]) 106 called[i]+=size[k],call[k]++; 107 } 108 } 109 for(i=1;i<=m;i++)outx(call[endpos[i]]),putchar(' '); 110 for(i=1;i<=n;i++){ 111 outx(called[i]);if(i<n)putchar(' '); 112 } 113 return 0; 114 }
具体实现有点烦。。。主要是要把姓名存下来。。我个傻逼写了一坨链式前向星,然后直接把所有东西都反过来处理了TAT
其实复杂度还是略玄学。。。理论上是可以被卡的?。。。但不是那(du)么(liu)好(lai)卡(le)吧= =