Luogu2336 SCOI2012 喵星球上的点名

Description

link

概述:

给定 \(n\) 个有姓有名的人的姓和名(数字串),然后给 \(m\) 个询问

每次询问都是一个数字串,如果询问里的串是某个人的姓或者名,那么这个人就被点到了

求出每个人被点到了几次,和每次询问点到了几个人

Solution

首先按照 Sandy 的卡片 这题的方法,我们可以把所有的串连起来

同时把 \(height\) 数组 \(ST\)预处理出来

我们发现每个询问串如果在姓名里面出现了,那么显然会是一个后缀的前缀

后缀排序后,如果真的存在是子串的情况,那么会是一段后缀区间的子串

这个性质相当显然

然后我们二分求出这个区间(这里判断的方法就是 \(lcp\ge len\) ),记为\([l,r]\)

(以上是对于询问的处理)

然后看题目,每次询问点到了几个人

那我们标记每个后缀属于哪个人,然后问题变成了一段区间里有多少个不同的姓名

用 HH的项链 的各种解法均可处理这个问题

这里考虑树状数组吧,顺便把询问按照右端点排了序,还方便后面一问的

下一问要求每个点被哪些区间覆盖了

我们把所有的区间的贡献在开始的时候放上去

然后遍历整个连完了的串,如果当前位置已经超过询问的右端点就把询问的贡献消掉

接着考虑每个姓名的贡献,对于一个人,记录其后缀上次出现的位置,然后一个新的点,拿前缀和减去上次的就行了

Code

#include
using namespace std;
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=1e6+10;
	int sa[N],h[N][20],rk[N],x[N],y[N],lg[N],num,Q,n,m,cnt;
	int len[N],app[N],b[N],las[N],ans[N],pos[N];
	struct node{int l,r,id;}s[N];
	inline bool cmp(node a,node b){return a.r=1;--i) sa[y[rk[x[i]]]--]=x[i];
		return ;
	}
	inline void get_sa()
	{
		m=5e5;
		for(int i=1;i<=n;++i) rk[i]=b[i],x[i]=i; qsort();
		for(int w=1,p=0;pw) x[++p]=sa[i]-w;
			qsort(); swap(rk,x); rk[sa[1]]=p=1;
			for(int i=2;i<=n;++i) 
				if(x[sa[i-1]]==x[sa[i]]&&x[sa[i-1]+w]==x[sa[i]+w]) rk[sa[i]]=p;
				else rk[sa[i]]=++p;
		} return ;
	}
	inline void get_h()
	{
		int k=0;
		for(int i=1;i<=n;++i) 
		{
			if(rk[i]==1){h[1][0]=k=0; continue;}
			if(k) --k; int j=sa[rk[i]-1];
			while(i+k<=n&&j+k<=n&&b[i+k]==b[j+k]) ++k; 
			h[rk[i]][0]=k;
		}
		for(int j=1;j<=18;++j)
		{
			for(int i=1;i+(1<>1;
			if(query(mid,rk[app[id]])>1;
			if(query(rk[app[id]],mid)>1]+1;
		num=read(); Q=read();
		for(int i=1;i<=num;++i)
		{
			len[++cnt]=read(); app[cnt]=n+1;
			for(int j=1;j<=len[cnt];++j) b[++n]=read(),pos[n]=cnt; b[++n]=cnt+1e4;
			len[++cnt]=read(); app[cnt]=n+1;
			for(int j=1;j<=len[cnt];++j) b[++n]=read(),pos[n]=cnt; b[++n]=cnt+1e4;
		}
		for(int i=1;i<=Q;++i)
		{
			len[++cnt]=read(); app[cnt]=n+1;
			for(int j=1;j<=len[cnt];++j) b[++n]=read(),pos[n]=cnt; b[++n]=cnt+1e4;
		} 
		get_sa(); get_h(); 
		for(int i=1;i<=Q;++i) calc(2*num+i),s[i].id=i;
		sort(s+1,s+Q+1,cmp);
		for(int i=1,j=1;i<=Q;++i)
		{
			while(j<=s[i].r) 
			{
				if(pos[sa[j]]<=2*num) 
				{
					int tmp=(pos[sa[j]]+1)>>1;
					if(las[tmp]) T.add(las[tmp],-1);
					las[tmp]=j;
					T.add(j,1); 
				}++j;
			} ans[s[i].id]=T.ask(s[i].r)-T.ask(s[i].l-1);
		}
		for(int i=1;i<=Q;++i) printf("%d\n",ans[i]);
		memset(ans,0,sizeof(ans)); memset(las,0,sizeof(las));
		memset(T.c,0,sizeof(T.c));
		for(int i=1;i<=Q;++i) T.add(s[i].l,1);
		for(int i=1,j=1;i<=n;++i)
		{
			while(j<=Q&&s[j].r>1; 
				ans[t]+=T.ask(i)-T.ask(las[t]);
				las[t]=i;
			}
		} for(int i=1;i<=num;++i) printf("%d ",ans[i]); puts("");
		return 0;
	}
}
signed main(){return yspm::main();}

你可能感兴趣的:(Luogu2336 SCOI2012 喵星球上的点名)