写了5kb,AC之后去查题解,发现居然暴力就可以过了,而且写的好的暴力比我的代码跑的快……这题卡暴力的数据随手造,出题人没卡什么心态,考场上写正解的人岂不是玩脱了……在网上没有找到写正解的题解,都是和答案大小相关的暴力(有些版本只有第二问和答案大小相关),于是我在这里简要写一下正解好了。虽然这种题肯定之前有人是写了正解的,不过不知道是他们没写题解还是百度搜不到……
首先把所有的喵星人的姓名串接起来,然后做SA,对于每个教师的点名,找出来一个极大范围[l,r]使得后缀sa[l]~sa[r]的前缀可以和教师的点名匹配。
把位置i的权值定义为sa[i]属于第几号喵星人,于是第一问就变成了询问[l,r]之间有几个不同的数,经典问题。
第二问的话,我们把教师的点名看成一个覆盖了[l,r]的线段,那么问题就变成了权值相同的所有点,被多少个不同的线段覆盖。直接做有点难,我们先忽略线段不同的限制,也就是计算被覆盖的次数而不是被几条线段覆盖,这个显然是可以随便做的。接下来我们只要把多算的那些次数减掉,整道题就完了。
多的那些次数怎么计算呢……定义pre[i]为与位置i权值相同的前一个位置。假如我们采取如下的方式统计“权值相同的所有点,被多少个不同的线段覆盖”:对于同一条线段和权值相同的所有点,我们只在线段能覆盖的第一个点处统计这条线段,那么按照上一段的算法,我们在每个位置i处多算的线段数=能完全覆盖[pre[i],i]的线段,这个玩意也是可以随便搞搞就做掉。
首先把所有的喵星人的姓名串接起来,然后做SA,对于每个教师的点名,找出来一个极大范围[l,r]使得后缀sa[l]~sa[r]的前缀可以和教师的点名匹配。
把位置i的权值定义为sa[i]属于第几号喵星人,于是第一问就变成了询问[l,r]之间有几个不同的数,经典问题。
第二问的话,我们把教师的点名看成一个覆盖了[l,r]的线段,那么问题就变成了权值相同的所有点,被多少个不同的线段覆盖。直接做有点难,我们先忽略线段不同的限制,也就是计算被覆盖的次数而不是被几条线段覆盖,这个显然是可以随便做的。接下来我们只要把多算的那些次数减掉,整道题就完了。
多的那些次数怎么计算呢……定义pre[i]为与位置i权值相同的前一个位置。假如我们采取如下的方式统计“权值相同的所有点,被多少个不同的线段覆盖”:对于同一条线段和权值相同的所有点,我们只在线段能覆盖的第一个点处统计这条线段,那么按照上一段的算法,我们在每个位置i处多算的线段数=能完全覆盖[pre[i],i]的线段,这个玩意也是可以随便搞搞就做掉。
此题至此解决,时间复杂度O(xlogx)(x=n,m,字符串总长……)。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN=20005,MAXM=50005,MAXL=100005,MAXLEN=MAXN*2+MAXL;
struct segment
{
int l,r,no;
}cat[MAXLEN],tch[MAXM];
int maxbit,l,s[MAXLEN],s2[MAXL],a[MAXLEN],b[MAXLEN],nxt[MAXLEN],last[MAXN],tag[MAXLEN],C[MAXLEN],d[20][MAXLEN],delta[MAXLEN],height[MAXLEN],rk[MAXLEN],sa[MAXLEN],t[MAXLEN],t2[MAXLEN],c[MAXLEN],anst[MAXM];
long long ansc[MAXN];
bool cmp(const segment &a,const segment &b)
{
return a.l<b.l;
}
inline void getname(int id)
{
int k;
scanf("%d",&k);
for (int c;k--;)
{
scanf("%d",&c);
a[l]=id;
s[l++]=c;
}
s[l++]=10001;
}
inline void build_sa(int m)
{
int *x=t,*y=t2;
for (int i=0;i<l;i++) c[x[i]=s[i]]++;
for (int i=1;i<m;i++) c[i]+=c[i-1];
for (int i=l-1;i>=0;i--) sa[c[x[i]]--]=i;
for (int k=1;k<l;k<<=1)
{
int p=0;
for (int i=l-k;i<l;i++) y[p++]=i;
for (int i=1;i<=l;i++) if (sa[i]>=k) y[p++]=sa[i]-k;
for (int i=0;i<m;i++) c[i]=0;
for (int i=0;i<l;i++) c[x[i]]++;
for (int i=1;i<m;i++) c[i]+=c[i-1];
for (int i=l-1;i>=0;i--) sa[c[x[y[i]]]--]=y[i];
swap(x,y);
p=1;x[sa[1]]=0;
for (int i=2;i<=l;i++)
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
if (p>=l) break;
m=p;
}
}
inline void getheight()
{
for (int i=1;i<=l;i++) rk[sa[i]]=i;
for (int i=0,k=0,j;i<l;i++)
{
if (rk[i]==1)
{
k=0;
continue;
}
if (k) k--;
j=sa[rk[i]-1];
while (s[i+k]==s[j+k]) k++;
height[rk[i]]=k;
}
}
inline void RMQ_init()
{
while ((1<<maxbit)<=l) maxbit++;
maxbit--;
delta[1]=0;
for (int i=2;i<=l;i++) delta[i]=delta[i>>1]+1;
for (int i=2;i<=l;i++) d[0][i]=height[i];
for (int j=1;j<=maxbit;j++)
for (int i=2;i+(1<<j)-1<=l;i++)
d[j][i]=min(d[j-1][i],d[j-1][i+(1<<j-1)]);
}
inline int cmp_suffix(int bg,int len)
{
for (int i=bg;i<l&&i-bg<len;i++)
if (s[i]!=s2[i-bg])
return s[i]<s2[i-bg]?-1:1;
return 0;
}
inline int RMQ(int l,int r)
{
int k=delta[r-l+1];
return min(d[k][l],d[k][r-(1<<k)+1]);
}
inline int LCP(int l,int r)
{
if (r==0) return 0;
if (l==r) return MAXLEN;
if (l>r) swap(l,r);
return RMQ(l+1,r);
}
inline int check(int len)
{
int lt=1,rt=l,now=0,nowp=0;
if (cmp_suffix(sa[lt],len)>0) return 0;
if (cmp_suffix(sa[rt],len)<0) return 0;
while (lt<=rt)
{
int m=lt+rt>>1;
int lcp=LCP(m,now);
if (lcp<nowp)
{
if (m<now) lt=m+1;
else rt=m-1;
}
else
{
while (nowp<len&&s[sa[m]+nowp]==s2[nowp]) nowp++;
if (nowp==len) return m;
now=m;
if (s[sa[m]+nowp]<s2[nowp]) lt=m+1;
else rt=m-1;
}
}
return 0;
}
inline int getl(int x,int lcp)
{
int lt=1,rt=x;
while (lt<=rt)
{
int m=lt+rt>>1;
if (LCP(m,x)<lcp) lt=m+1;
else rt=m-1;
}
return lt;
}
inline int getr(int x,int lcp)
{
int lt=x,rt=l;
while (lt<=rt)
{
int m=lt+rt>>1;
if (LCP(x,m)<lcp) rt=m-1;
else lt=m+1;
}
return rt;
}
inline void add(int x,int d)
{
while (x<=l)
{
C[x]+=d;
x+=x&-x;
}
}
inline int sum(int x)
{
int ret=0;
while (x)
{
ret+=C[x];
x-=x&-x;
}
return ret;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
getname(i);
getname(i);
}
build_sa(10002);
for (int i=1;i<=l;i++) b[i]=a[sa[i]];
int cntc=0;
for (int i=1;i<=l;i++)
{
if (b[i]==0) continue;
if (last[b[i]])
{
cat[cntc].l=last[b[i]];
cat[cntc].r=i;
cat[cntc].no=b[i];
cntc++;
}
else add(i,1);
last[b[i]]=i;
}
fill(last,last+n+1,l+1);
for (int i=l;i>=1;i--)
{
nxt[i]=last[b[i]];
last[b[i]]=i;
}
getheight();
RMQ_init();
int cnt=0;
for (int i=0;i<m;i++)
{
int len;
scanf("%d",&len);
for (int j=0;j<len;j++) scanf("%d",&s2[j]);
int ret=check(len);
if (ret)
{
tch[cnt].l=getl(ret,len);
tch[cnt].r=getr(ret,len);
tch[cnt].no=i;
tag[tch[cnt].l]++;
tag[tch[cnt].r+1]--;
cnt++;
}
}
sort(tch,tch+cnt,cmp);
for (int i=0,head=1;i<cnt;i++)
{
while (head<tch[i].l)
{
if (b[head])
{
add(head,-1);
add(nxt[head],1);
}
head++;
}
anst[tch[i].no]=sum(tch[i].r);
}
for (int i=0;i<m;i++) printf("%d\n",anst[i]);
for (int i=1,sum=0;i<=l;i++)
{
sum+=tag[i];
ansc[b[i]]+=sum;
}
sort(cat,cat+cntc,cmp);
fill(C,C+l+1,0);
for (int i=0,head=0;i<cntc;i++)
{
while (head<cnt&&tch[head].l<=cat[i].l) add(tch[head++].r,1);
ansc[cat[i].no]-=head-sum(cat[i].r-1);
}
for (int i=1;i<n;i++) printf("%lld ",ansc[i]);
printf("%lld",ansc[n]);
return 0;
}