P2336 [SCOI2012]喵星球上的点名 题解
题目描述
有 \(n\) 个喵星人,每个人的名字由姓和名两个字符串构成。
有 \(m\) 个点名串,如果这个串是某个喵星人的姓或名的子串,那么这个喵星人必须答到。
对每个点名串,求有多少喵星人答到。
对每个喵星人,求它答到多少次。
数据范围
- \(1\le n\le 5\cdot 10^4,1\le m\le 10^5\) ,保证名字长度和与点名串长度和分别 \(\le 10^5\) ,字符集大小为 \(10^4\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{125MB}\) 。
分析
对每个喵星人,将姓和名拼接起来,中间插入一个不存在的字符,那么当且仅当点名串(记为 \(t_j\) )是这个新字符串(记为 \(s_i\) )的子串时才会答到。
将所有 \(s_i\) 和 \(t_j\) 拼接起来,中间插入一个不存在的字符,并建立后缀数组。
将 \(s_i\) 的所有字符染成第 \(i\) 种颜色,注意到子串等价于后缀的前缀。
对于每个 \(t_j\) ,在 sa[] 上二分找到满足 \(\texttt{lcp}\ge|t_j|\) 的左右端点,那么区间内所有颜色对应的喵星人都要答到。
于是第一问变成了区间数颜色,记录上一次出现相同颜色的位置 \(pre_i\) ,离线后在位置 \(pre_i\) 删除贡献,在位置 \(i\) 加入贡献,询问就是求后缀和。
对于第二问,我们要求每种颜色与多少个区间有交。
考虑增量计算贡献,即求有多少区间与 \(i\) 有交,并且与 \(pre_i\) 无交。
离线扫描线维护所有与当前位置 \(i\) 有交的区间,在扫描到区间的左右端点时更新。
那么区间 \([l,r]\) 与 \(pre_i\) 无交等价于 \(l\gt pre_i\) ,用树状数组维护所有的 \(l\) ,询问依然是后缀和。
时间复杂度 \(\mathcal O((n+m)\log n)\) 。
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int v=1e4+5,maxn=4e5+5;
int l,m,n;
int c[maxn],x[maxn],y[2*maxn];
int h[maxn],rk[maxn],sa[maxn];
int lg[maxn],f[19][maxn];
int s[maxn],col[maxn];
int lst[maxn],pre[maxn],res[maxn];
pii p[maxn],q[maxn];
vector<pii> v1[maxn],v2[maxn];
void read()
{
static int k=0;
scanf("%d",&k);
while(k--) scanf("%d",&s[++l]);
s[++l]=v;
}
void get_sa(int n)
{
int m=v;
for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
for(int k=1;k<=n;k<<=1)
{
int num=0;
for(int i=n-k+1;i<=n;i++) y[++num]=i;
for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
for(int i=1;i<=m;i++) c[i]=0;
for(int i=1;i<=n;i++) c[x[i]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i];
for(int i=1;i<=n;i++) y[i]=x[i],x[i]=0;
x[sa[1]]=num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?num:++num;
if(num==n) break;
m=num;
}
}
void get_height(int n)
{
for(int i=1;i<=n;i++) rk[sa[i]]=i;
s[0]=-1;
for(int i=1,k=0;i<=n;i++)
{
if(k) k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
h[rk[i]]=k;
}
}
inline int lcp(int l,int r)
{
int k=lg[r-l+1];
return min(f[k][l],f[k][r-(1<<k)+1]);
}
inline void add(int x,int v)
{
while(x) c[x]+=v,x-=x&(-x);
}
inline int query(int x)
{
int res=0;
while(x<=l) res+=c[x],x+=x&(-x);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) p[i].fi=l+1,read(),read(),p[i].se=l-1;
for(int i=1;i<=m;i++) q[i].fi=l+1,read(),q[i].se=l-1;
get_sa(l),get_height(l);
for(int i=2;i<=l;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=l;i++) f[0][i]=h[i];
for(int j=1;j<=18;j++)
for(int i=1;i+(1<<j)-1<=l;i++)
f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
for(int i=1;i<=m;i++)
{
int len=q[i].se-q[i].fi+1,pos=rk[q[i].fi];
auto get_l=[&]()
{
int l=0,r=pos;
while(r-l>1)
{
int mid=(l+r)>>1;
lcp(mid+1,pos)>=len?r=mid:l=mid;
}
return r;
};
auto get_r=[&]()
{
int l=pos,r=::l+1;
while(r-l>1)
{
int mid=(l+r)>>1;
lcp(pos+1,mid)>=len?l=mid:r=mid;
}
return l;
};
int l=get_l(),r=get_r();
v1[r].push_back(mp(l,i));
v2[l].push_back(mp(l,1)),v2[r+1].push_back(mp(l,-1));
}
for(int i=1;i<=n;i++) for(int j=p[i].fi;j<=p[i].se;j++) col[rk[j]]=i;
for(int i=1;i<=l;i++) if(col[i]) pre[i]=lst[col[i]],lst[col[i]]=i;
memset(c,0,sizeof(c));
for(int i=1;i<=l;i++)
{
if(col[i]) add(pre[i],-1),add(i,1);
for(auto p:v1[i]) res[p.se]=query(p.fi);
}
for(int i=1;i<=m;i++) printf("%d\n",res[i]);
memset(c,0,sizeof(c));
memset(res,0,sizeof(res));
for(int i=1;i<=l;i++)
{
for(auto p:v2[i]) add(p.fi,p.se);
if(col[i]) res[col[i]]+=query(pre[i]+1);
}
for(int i=1;i<=n;i++) printf("%d ",res[i]);
putchar('\n');
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17489198.html
浙公网安备 33010602011771号